Ai_GirlFriend/xuniYou/pages/login/index.vue
2026-02-04 18:58:05 +08:00

828 lines
19 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.

<template>
<view>
<uni-nav-bar fixed statusBar background-color="transparent" :border="false" @clickLeft="back"></uni-nav-bar>
<view class="back"></view>
<view class="body">
<image class="logo"
src="https://nvlovers.oss-cn-qingdao.aliyuncs.com/uploads/20251226/7d4ea9bb621e68e9fc641fa3c357f678.png"
mode="aspectFill"></image>
<view class="list">
<view class="list_content">
<view class="list_module fa">
<image src="/static/images/login_phone.png" mode="widthFix"></image>
<input class="f1" v-model="form.mobile" placeholder="请输入手机号" placeholder-class="list_input" />
</view>
<!-- <view class="list_detail faj">
<image src="/static/images/login_errors.png" mode="widthFix"></image>
<view class="list_errors">手机号输入错误,请重新输入</view>
</view> -->
</view>
<view class="list_content" v-if="!isDev">
<view class="list_module fa">
<image src="/static/images/login_password.png" mode="widthFix"></image>
<input type="password" class="f1" v-model="form.password" placeholder="请输入密码"
placeholder-class="list_input" />
</view>
<!-- <view class="list_detail faj">
<image src="/static/images/login_errors.png" mode="widthFix"></image>
<view class="list_errors">密码输入错误,请重新输入</view>
</view> -->
</view>
<!-- 开发模式提示 -->
<view v-if="isDev" class="dev-tip">
<text>🔧 开发模式:只需输入手机号即可登录</text>
</view>
<!-- 邀请码输入框 -->
<view class="list_content">
<view class="list_module fa">
<image src="/static/images/login_code.png" mode="widthFix"></image>
<input class="f1" v-model="inviteCode" placeholder="邀请码(选填)"
placeholder-class="list_input" />
</view>
<view v-if="inviteCode" class="invite-tip">
<text>✅ 使用邀请码可获得5金币奖励</text>
</view>
</view>
<!-- <view class="list_content">
<view class="list_module fa">
<image src="/static/images/login_code.png" mode="widthFix"></image>
<input class="f1" v-model="form.captcha" placeholder="请输入手机验证码"
placeholder-class="list_input" />
<view class="list_obtain faj" @click="codeClick()">获取验证码</view>
</view>
<view class="list_detail faj">
<image src="/static/images/login_errors.png" mode="widthFix"></image>
<view class="list_errors">验证码输入错误,请重新输入</view>
</view>
</view> -->
<view class="list_logo faj" :class="{ 'active': isInputFilled }" @click="submit()">立即登录</view>
<view class="list_prompt fa sb">
<view class="list_text">首次登录即为注册</view>
<view class="list_forgotPassword" @click="toforgotPassword()">忘记密码?</view>
</view>
</view>
<view class="module fa sb">
<view class="module_content f1 faj fc">
<!-- #ifdef MP-WEIXIN -->
<button class="module_weixin faj fc" v-if="dataLogin.id == 0" open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber" :plain="true" hover-class="none">
<image src="/static/images/login_weixin.png" mode="widthFix"></image>
<view class="module_title">微信登录</view>
</button>
<view v-else class="f1 faj fc" @click="wxphoneloginClcik">
<image src="/static/images/login_weixin.png" mode="widthFix"></image>
<view class="module_title">微信登录</view>
</view>
<!-- #endif -->
<!-- #ifdef APP -->
<view class="f1 faj fc" @click="wxphoneloginClcik1">
<image src="/static/images/login_weixin.png" mode="widthFix"></image>
<view class="module_title">微信登录</view>
</view>
<!-- #endif -->
</view>
<view class="module_content f1 faj fc" @click="tiktokLogin()">
<image src="/static/images/login_douyin.png" mode="widthFix"></image>
<view class="module_title">抖音登录</view>
</view>
</view>
<view class="opt">
<view class="opt_content">
<view class="opt_module faj">
<view class="opt_select faj" @click="selectClick()">
<image :src="selectStats ? '/static/images/selectA.png' : '/static/images/select.png'"
mode="widthFix"></image>
</view>
<view class="opt_text">
<view class="opt_name">阅读并同意<text>《运营商认证服务协议》、</text></view>
<view class="opt_name"><text>《软件许可及服务协议》</text>和<text>《隐私协议》</text></view>
</view>
<view class="opt_prompt faj">
<view class="opt_tip faj"><text></text>请先勾选协议</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
Mobilelogin,
Wxapplogin,
Send,
WxappGetPhone,
AppLoginWx
} from '@/utils/api.js'
import { baseURLPy } from '@/utils/request.js'
import {
isPhone,
getWxCode
} from '@/utils/utils.js'
import notHave from '@/components/not-have.vue';
import topSafety from '@/components/top-safety.vue';
export default {
components: {
notHave,
topSafety,
},
data() {
return {
isDev: true, // 开发模式开关,上线时改为 false
statusBarHeight: uni.getWindowInfo().statusBarHeight,
form: {
mobile: '',
password: '',
captcha: 223344,
wxapp_code: '',
},
formregister: {
mobile: '',
event: 'register',
},
dataLogin: [],
selectStats: false,
inviteCode: '', // 邀请码
baseURLPy: baseURLPy,
}
},
computed: {
isInputFilled() {
return this.form.mobile.trim() !== '' && this.form.password.trim() !== '';
}
},
onLoad(options) {
console.log('登录页面')
// 从 URL 参数获取邀请码
if (options && options.invite) {
this.inviteCode = options.invite;
uni.showToast({
title: '已自动填入邀请码',
icon: 'success'
});
}
// #ifdef MP
this.Login()
// #endif
},
methods: {
async Login() {
console.log('Login',)
let code = await getWxCode();
Wxapplogin({
code: code
}).then(res => {
console.log('code换取',res)
this.dataLogin = res.data
console.log(this.dataLogin)
}).catch(err => {
uni.showToast({
title: "登录失败,请重试",
icon: 'none',
position: 'top'
})
})
},
// 微信手机号登录
getPhoneNumber(e) {
if (!this.selectStats) {
uni.showToast({
title: "请阅读并同意协议",
icon: 'none',
position: 'top'
})
return;
}
WxappGetPhone({
code: e.detail.code,
login_token: this.dataLogin.login_token,
}).then(res => {
console.log(res)
console.log(res.code)
console.log(res.msg)
if (res.code == 1) {
uni.setStorageSync("token", res.data.userinfo.token)
uni.setStorageSync("userinfo", res.data.userinfo)
uni.showToast({
title: res.msg,
icon: 'success',
position: 'top'
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
})
}, 1000)
} else {
uni.showToast({
title: res.msg,
icon: 'none',
position: 'top'
})
}
}).catch(err => {
uni.showToast({
title: "登录失败,请重试",
icon: 'none',
position: 'top'
})
})
},
wxphoneloginClcik1() {
let that = this;
uni.getProvider({
service: 'oauth',
success: function(res) {
console.log('是否安装了微信', res.provider);
},
fail(e) {
uni.showToast({ title:e.errMsg, icon:'none' })
}
});
if (!this.selectStats) {
uni.showToast({
title: "请阅读并同意协议",
icon: 'none',
position: 'top'
})
return;
}
uni.showLoading({
title:'登录中...',
mask:true
})
uni.login({
provider: 'weixin',
success: (res) => {
console.log('微信登陆', res)
uni.getUserInfo({
provider: 'weixin',
success: (infoRes) => {
console.log('infoRes', infoRes)
let userInfo = infoRes.userInfo
console.log('openid:', userInfo.openId, 'unionid:', userInfo.unionId)
// uni.showLoading({ title: "登录中", mask: true })
// 利用微信授权获取到的用户信息 请求登录
// 判断是否注册
// that.upImage(userInfo);
that.app_login(userInfo)
}
})
},
fail: (err) => {
uni.hideLoading()
console.log('微信登陆失败', err)
if (err.code == -2) {
this.setShowToast(err.errMsg)
}
if (err.code == '-100') {
// requestPayment:fail [payment支付宝:62001]用户中途取消支付操作
this.setShowToast(err.errMsg)
}
}
})
},
app_login(userInfo){
console.log('userInfo',userInfo)
AppLoginWx({
openid: userInfo.openId,//'oRrdQt41cXAihKalNnuulzIVGMEs',//
}).then(res => {
console.log('APP微信登录',res)
let data = res.data
if(res.code == 1){
this.dataLogin = data
if(data.id == 0){
uni.showToast({
title:'继续绑定手机号登录',
icon:'none'
})
}else{
this.wxphoneloginClcik()
}
uni.hideLoading()
}
}).catch(err => {
uni.showToast({
title: "登录失败,请重试",
icon: 'none',
position: 'top'
})
})
},
wxphoneloginClcik() {
if (!this.selectStats) {
uni.showToast({
title: "请阅读并同意协议",
icon: 'none',
position: 'top'
})
return;
}
uni.setStorageSync("token", this.dataLogin.userinfo.token)
uni.setStorageSync("userinfo", this.dataLogin.userinfo)
uni.showToast({
title: '登录成功',
icon: 'success',
position: 'top'
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
})
}, 1000)
},
tiktokLogin() {
// TODO: 实现抖音登录逻辑
uni.showToast({
title: '抖音登录功能即将开放',
icon: 'none'
});
},
codeClick() {
// 验证手机号
if (this.form.mobile == '') {
uni.showToast({
title: "请输入手机号",
icon: 'none',
position: 'top'
})
return;
} else if (!isPhone(this.form.mobile)) {
uni.showToast({
icon: "none",
title: "请输入正确手机号"
})
return;
}
this.formregister.mobile = this.form.mobile;
this.send();
},
send() {
Send(this.formregister).then(res => {
if (res.code == 1) {
uni.showToast({
title: res.msg,
icon: 'none',
position: 'top'
})
} else {
uni.showToast({
title: res.msg,
icon: 'none',
position: 'top'
})
}
}).catch(err => {
uni.showToast({
title: "验证码发送失败",
icon: 'none',
position: 'top'
})
})
},
async mobilelogin() {
// #ifdef mp-weixin
let code = await getWxCode();
this.form.wxapp_code = loginRes.code
// #endif
// #ifdef APP
if(this.dataLogin.id == 0 && this.dataLogin.login_token){
this.form.login_token = this.dataLogin.login_token
}
// #endif
Mobilelogin(this.form).then(res => {
if (res.code == 1) {
uni.showToast({
title: res.msg,
icon: 'none',
position: 'top'
})
uni.setStorageSync("token", res.data.userinfo.token)
uni.setStorageSync("userinfo", res.data.userinfo)
// 如果有邀请码,尝试使用
if (this.inviteCode && this.inviteCode.trim()) {
this.applyInviteCode();
} else {
setTimeout(() => {
uni.navigateTo({
url: '/pages/index/index'
})
}, 1000);
}
} else if (res.code == 0) {
console.log('提示错误信息')
console.log(res)
uni.showToast({
title: res.msg,
icon: 'none',
position: 'top'
})
}
})
},
// 使用邀请码
applyInviteCode() {
const token = uni.getStorageSync("token");
console.log('准备使用邀请码:', this.inviteCode);
console.log('当前 token:', token);
if (!token) {
console.error('Token 不存在,无法使用邀请码');
uni.showToast({
title: '登录状态异常,请重新登录',
icon: 'none'
});
setTimeout(() => {
uni.navigateTo({
url: '/pages/index/index'
})
}, 1500);
return;
}
uni.request({
url: this.baseURLPy + '/config/invite/apply',
method: 'POST',
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
data: {
invite_code: this.inviteCode
},
success: (res) => {
console.log('邀请码使用响应:', res);
if (res.data && res.data.code === 1) {
uni.showToast({
title: res.data.data.message || '邀请码使用成功',
icon: 'success'
});
} else {
// 邀请码使用失败,显示错误信息
const errorMsg = res.data ? res.data.message : '邀请码使用失败';
console.log('邀请码使用失败:', errorMsg);
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
});
}
},
fail: (err) => {
console.error('邀请码请求失败:', err);
uni.showToast({
title: '网络错误,邀请码使用失败',
icon: 'none'
});
},
complete: () => {
// 无论成功失败,都跳转到首页
setTimeout(() => {
uni.navigateTo({
url: '/pages/index/index'
})
}, 1500);
}
});
},
selectClick() {
if (this.selectStats) {
this.selectStats = false
} else {
this.selectStats = true
}
},
submit() {
// 使用 data 中的开发模式开关
if (!this.selectStats) {
uni.showToast({
title: "请阅读并同意协议",
icon: 'none',
position: 'top'
})
return;
}
if (this.form.mobile == '') {
uni.showToast({
title: "请输入手机号",
icon: 'none',
position: 'top'
})
return;
}
if (!isPhone(this.form.mobile)) {
uni.showToast({
icon: "none",
title: "请输入正确手机号"
})
return;
}
// 开发环境:跳过密码和验证码验证
if (this.isDev) {
console.log('开发模式:跳过密码验证');
// 自动填充密码和验证码
this.form.password = this.form.password || '123456';
this.form.captcha = this.form.captcha || 223344;
this.mobilelogin();
return;
}
// 生产环境:正常验证
if (this.form.password == '') {
uni.showToast({
title: "请输入密码",
icon: 'none',
position: 'top'
})
return;
}
if (this.form.captcha == '') {
uni.showToast({
title: "请输入验证码",
icon: 'none',
position: 'top'
})
return;
}
this.mobilelogin();
},
back() {
uni.navigateBack({
delta: 1,
});
},
toforgotPassword() {
uni.navigateTo({
url: '/pages/login/password'
})
}
}
}
</script>
<style>
.body {
position: relative;
}
.back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 436rpx;
background: linear-gradient(180deg, #EAD6FF 0%, rgba(255, 255, 255, 0) 100%);
}
.body {
position: relative;
padding: 0 60rpx;
}
.logo {
position: relative;
margin: 136rpx auto 0 auto;
width: 166rpx;
height: 166rpx;
border-radius: 28rpx;
display: block;
}
.list {
position: relative;
margin: 124rpx 0 0 0;
}
.list_content {
position: relative;
margin: 72rpx 0 0 0;
}
/* 开发模式提示 */
.dev-tip {
margin: 30rpx 0;
padding: 20rpx 30rpx;
background: linear-gradient(90deg, #FFF3E0, #FFE0B2);
border-radius: 12rpx;
border-left: 6rpx solid #FF9800;
}
.dev-tip text {
font-size: 26rpx;
color: #E65100;
line-height: 40rpx;
}
/* 邀请码提示 */
.invite-tip {
margin-top: 10rpx;
padding: 10rpx 20rpx;
background: #E8F5E9;
border-radius: 8rpx;
}
.invite-tip text {
font-size: 24rpx;
color: #2E7D32;
}
.list_module {
position: relative;
padding: 20rpx 30rpx;
background: #F9F9F9;
border-radius: 16rpx;
}
.list_module image {
margin: 0 20rpx 0 0;
width: 48rpx;
height: 48rpx;
}
.list_module input {
font-weight: 500;
font-size: 30rpx;
color: #000000;
line-height: 42rpx;
}
.list_logo.active {
color: #FFFFFF;
background: #9F47FF;
}
.list_content:nth-child(3) .list_module {
padding: 12rpx 30rpx;
}
.list_input {
color: #9E9E9E;
}
.list_detail {
position: absolute;
left: 0;
bottom: -60rpx;
padding: 8rpx 12rpx 6rpx 12rpx;
font-weight: 400;
font-size: 28rpx;
color: #FF0A1F;
line-height: 42rpx;
background: #FFF6F7;
border-radius: 4rpx;
}
.list_detail image {
margin: 0 14rpx 0 0;
width: 28rpx;
height: 28rpx;
}
.list_obtain {
padding: 8rpx 20rpx;
font-weight: 400;
font-size: 28rpx;
color: #9F47FF;
line-height: 42rpx;
background: #F3E9FF;
border-radius: 16rpx;
}
.list_logo {
position: relative;
margin: 66rpx 0 0 0;
padding: 24rpx 0;
font-weight: 500;
font-size: 30rpx;
color: #9E9E9E;
line-height: 42rpx;
background: #F9F9F9;
border-radius: 16rpx;
}
.list_prompt {
position: relative;
margin: 30rpx 0 0 0;
font-weight: 400;
font-size: 28rpx;
color: #9E9E9E;
line-height: 42rpx;
}
.module {
position: relative;
margin: 76rpx 0 0 0;
}
.module_content {
position: relative;
}
.module_weixin {
border: none !important;
}
.module_content image {
width: 88rpx;
height: 88rpx;
display: block;
border-radius: 100rpx;
}
.module_title {
margin: 20rpx 0 0 0;
font-weight: 400;
font-size: 28rpx;
color: #9E9E9E;
line-height: 50rpx;
}
.opt {
position: fixed;
bottom: 30rpx;
left: 0;
right: 0;
margin: 0 auto;
}
.opt_content {
position: relative;
}
.opt_module {
position: relative;
}
.opt_select {
position: relative;
}
.opt_select image {
margin: 0 24rpx 0 0;
width: 32rpx;
height: 32rpx;
}
.opt_prompt {
position: absolute;
left: 0;
right: 0;
top: -100rpx;
margin: 0 auto;
}
.opt_tip {
position: relative;
padding: 14rpx 0;
width: 222rpx;
font-weight: 400;
font-size: 30rpx;
color: #FFFFFF;
line-height: 42rpx;
background: #9F47FF;
border-radius: 12rpx;
}
.opt_tip::after {
content: '';
position: absolute;
bottom: -8rpx;
left: 50%;
transform: translateX(-50%);
width: 16rpx;
height: 16rpx;
background: #9F47FF;
border-radius: 0 0 4rpx 0;
transform: translateX(-50%) rotate(45deg);
}
.opt_text {
font-weight: 500;
font-size: 26rpx;
color: #9E9E9E;
line-height: 42rpx;
}
.opt_text text {
color: #9F85FA;
}
.opt_name {
text-align: center;
}
</style>