HomeLease/pages/login/login.vue
2025-09-08 17:29:28 +08:00

567 lines
13 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 class="login-container">
<view :style="{ top: getNavBarHeight() + 'rpx' }" class="goback" @click="goback">
<uni-icons :size="getTitleBarHeight()" type="left"></uni-icons>
</view>
<view class="logo-section">
<image :src="CommonEnum.LOGIN_SRC" class="logo-image"></image>
</view>
<view class="main-content">
<!-- 登录按钮 -->
<button :disabled="loginLoading" class="login-btn" @click="goToLogin">
<text class="btn-text">{{ loginLoading ? '登录中...' : '微信用户一键登录' }}</text>
</button>
</view>
<!-- 底部协议 -->
<view class="agreement-section">
<view class="agreement-checkbox" @click="toggleAgreement">
<view :class="{ checked: hasAgreed }" class="checkbox">
<text v-if="hasAgreed" class="checkmark">✓</text>
</view>
</view>
<text class="agreement-text">我已阅读并同意</text>
<text class="agreement-link" @click="showServiceTerms">《服务协议》</text>
<text class="agreement-text">与</text>
<text class="agreement-link" @click="showPrivacyPolicy">《隐私政策》</text>
</view>
<!-- 服务条款弹窗 -->
<view v-if="showServiceTermsPopup" class="popup-overlay" @click="closeServiceTermsPopup">
<view class="popup-content" @click.stop>
<view class="popup-header">
<text class="popup-title">服务条款</text>
<text class="popup-close" @click="closeServiceTermsPopup">×</text>
</view>
<scroll-view
:scroll-top="scrollTop"
class="popup-body"
scroll-y="true"
show-scrollbar="true"
>
<view class="terms-content">
<rich-text :nodes="serviceTermsContent"></rich-text>
</view>
</scroll-view>
<view class="popup-footer">
<text class="popup-tip">请仔细阅读服务条款,阅读完毕后点击同意</text>
<button class="popup-btn" @click="agreeServiceTerms">同意</button>
</view>
</view>
</view>
<!-- 隐私政策弹窗 -->
<view v-if="showPrivacyPolicyPopup" class="popup-overlay" @click="closePrivacyPolicyPopup">
<view class="popup-content" @click.stop>
<view class="popup-header">
<text class="popup-title">隐私政策</text>
<text class="popup-close" @click="closePrivacyPolicyPopup">×</text>
</view>
<scroll-view
:scroll-top="scrollTop"
class="popup-body"
scroll-y="true"
show-scrollbar="true"
>
<view class="terms-content">
<rich-text :nodes="privacyPolicyContent"></rich-text>
</view>
</scroll-view>
<view class="popup-footer">
<text class="popup-tip">请仔细阅读隐私政策阅读完毕后点击同意</text>
<button class="popup-btn" @click="agreePrivacyPolicy">同意</button>
</view>
</view>
</view>
</view>
</template>
<script>
import { wxLogin } from '@/api/auth/auth.js'
import { forceHideLoading } from '@/utils/request.js'
import { getServiceTerms, getPrivacyPolicy } from '@/api/article/article.js'
import { commonEnum } from '../../enum/commonEnum'
import { getNavBarHeight, getTitleBarHeight } from '../../utils/system'
export default {
data() {
return {
loginLoading: false,
hasAgreed: false,
hasReadServiceTerms: false,
hasReadPrivacyPolicy: false,
showServiceTermsPopup: false,
showPrivacyPolicyPopup: false,
serviceTermsContent: '',
privacyPolicyContent: '',
scrollTop: 0,
agentId: '', // 添加agentId字段
}
},
computed: {
CommonEnum() {
return commonEnum
},
},
onLoad: async function (options) {
if (options && options.q) {
const q = decodeURIComponent(options.q) // 解码二维码链接
console.log('解析后的链接:', q) // 示例: https://wx.ccttiot.com/cf/i/?agentId=63
// 提取agentId参数
const agentId = this.getUrlParam('agentId', q)
if (agentId) {
this.agentId = agentId // 存入实例属性
console.log('成功存入agentId:', this.agentId)
} else {
console.log('获取id失败')
}
}
},
onUnload() {
forceHideLoading()
if (this.pageLoading) {
this.pageLoading.destroy()
}
},
methods: {
throttle() {
return throttle
},
getTitleBarHeight,
getNavBarHeight,
goback() {
uni.navigateBack()
},
getUrlParam(key, url) {
const regex = new RegExp(`[?&]${key}=([^&#]*)`)
const match = url.match(regex)
return match ? decodeURIComponent(match[1]) : null
},
toggleAgreement() {
this.hasAgreed = !this.hasAgreed
},
async showServiceTerms() {
try {
const res = await getServiceTerms()
if (res.code === 200 && res.data) {
let content = res.data.content || '暂无服务条款内容'
content = content.replace(/\n/g, '<br/>')
content = `<div style="word-wrap: break-word; word-break: break-all; line-height: 1.8; font-size: 28rpx; color: #333; padding: 0; margin: 0;">${content}</div>`
this.serviceTermsContent = content
this.showServiceTermsPopup = true
this.scrollTop = 0
} else {
uni.showToast({
title: '获取服务条款失败',
icon: 'none',
})
}
} catch (error) {
console.error('获取服务条款失败:', error)
uni.showToast({
title: '获取服务条款失败',
icon: 'none',
})
}
},
async showPrivacyPolicy() {
try {
const res = await getPrivacyPolicy()
if (res.code === 200 && res.data) {
let content = res.data.content || '暂无隐私政策内容'
content = content.replace(/\n/g, '<br/>')
content = `<div style="word-wrap: break-word; word-break: break-all; line-height: 1.8; font-size: 28rpx; color: #333; padding: 0; margin: 0;">${content}</div>`
this.privacyPolicyContent = content
this.showPrivacyPolicyPopup = true
this.scrollTop = 0
} else {
uni.showToast({
title: '获取隐私政策失败',
icon: 'none',
})
}
} catch (error) {
console.error('获取隐私政策失败:', error)
uni.showToast({
title: '获取隐私政策失败',
icon: 'none',
})
}
},
closeServiceTermsPopup() {
this.showServiceTermsPopup = false
},
closePrivacyPolicyPopup() {
this.showPrivacyPolicyPopup = false
},
async agreeServiceTerms() {
this.hasReadServiceTerms = true
this.closeServiceTermsPopup()
this.checkAgreementStatus()
},
async agreePrivacyPolicy() {
this.hasReadPrivacyPolicy = true
this.closePrivacyPolicyPopup()
this.checkAgreementStatus()
},
checkAgreementStatus() {
if (this.hasReadServiceTerms && this.hasReadPrivacyPolicy) {
this.hasAgreed = true
}
},
goToLogin() {
this.$uv.throttle(this.Login, 3000)
},
async Login() {
console.log('开始登录流程')
this.showLoading()
try {
// 1. 未同意显示协议确认弹窗
if (!this.hasAgreed) {
const confirmed = await this.showLoginConfirmation()
if (!confirmed) return
}
// 2. 获取微信登录code
const wxCode = await this.getWxLoginCode()
// 3. 执行登录请求
const loginResult = await this.executeLogin(wxCode)
// 4. 处理登录成功
this.handleLoginSuccess(loginResult)
} catch (error) {
this.handleLoginError(error)
} finally {
this.hideLoading()
}
},
// 显示加载状态
showLoading() {
this.pageLoading?.show('登录中...')
},
// 隐藏加载状态
hideLoading() {
this.pageLoading?.hide()
forceHideLoading()
},
// 显示登录确认弹窗
showLoginConfirmation() {
return new Promise(resolve => {
uni.showModal({
title: '确认登录',
content: '我已阅读并同意服务协议和隐私政策',
confirmText: '确定登录',
cancelText: '取消',
success: res => {
this.hasAgreed = res.confirm
resolve(res.confirm)
},
fail: err => {
console.error('弹窗操作失败:', err)
resolve(false)
},
})
})
},
// 获取微信登录code
getWxLoginCode() {
return new Promise((resolve, reject) => {
uni.login({
success: res => {
if (!res.code) {
reject(new Error('获取登录code失败'))
return
}
console.log('获取到登录code:', res.code)
resolve(res.code)
},
fail: err => {
reject(new Error(`微信登录失败: ${err.errMsg}`))
},
})
})
},
// 执行登录请求
async executeLogin(wxCode) {
console.log('携带的agentId:', this.agentId)
const loginData = {
loginCode: wxCode,
agentId: this.agentId,
}
const res = await wxLogin(loginData)
if (res.code !== 200) {
throw new Error(res.msg || '登录失败')
}
return res
},
// 处理登录成功
handleLoginSuccess(loginResult) {
console.log('登录成功:', loginResult)
uni.setStorageSync('token', loginResult.token)
uni.showToast({
title: '登录成功',
icon: 'success',
duration: 1500,
complete: () => {
uni.switchTab({
url: '/pages/index/index',
})
},
})
},
// 处理登录错误
handleLoginError(error) {
console.error('登录流程出错:', error)
uni.showToast({
title: error.message || '登录失败',
icon: 'none',
})
},
},
}
</script>
<style lang="scss" scoped>
page {
position: relative;
background: #ffffff;
}
.goback {
color: #e80a0a;
position: absolute;
left: 20rpx;
}
.login-container {
padding-bottom: 40rpx;
max-width: 750rpx;
background: #ffffff;
}
.logo-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 80rpx;
width: 100%;
margin-top: 330rpx;
//border:green 1px solid;
}
.logo-image {
width: 276rpx;
height: 276rpx;
}
.main-content {
padding: 0 53rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 654rpx;
//border: red 1px solid;
}
.login-btn {
width: 100%;
height: 98rpx;
background: #f15a04;
border-radius: 10rpx;
border: none;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);
}
.login-btn:active {
transform: translateY(2rpx);
box-shadow: 0 2rpx 8rpx rgba(255, 107, 53, 0.3);
}
.login-btn:disabled {
background: #ccc;
box-shadow: none;
}
.btn-text {
font-size: 32rpx;
font-weight: 600;
color: #fff;
}
.agreement-section {
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 30rpx;
margin-top: 40rpx;
flex-wrap: wrap;
.agreement-checkbox {
display: flex;
align-items: center;
.checkbox {
width: 28rpx;
height: 28rpx;
border: 2rpx solid #ff6b35;
border-radius: 6rpx;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
transition: all 0.3s ease;
.checkmark {
color: #fff;
font-size: 18rpx;
font-weight: bold;
}
}
.checkbox.checked {
background: #ff6b35;
border-color: #ff6b35;
}
}
.agreement-text {
font-size: 24rpx;
color: #666;
}
.agreement-link {
font-size: 24rpx;
color: #ff6b35;
text-decoration: underline;
}
}
.popup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
}
.popup-content {
background: #fff;
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
width: 100%;
max-width: 680rpx;
height: 85vh;
max-height: 900rpx;
min-height: 600rpx;
margin: 0;
}
.popup-header {
padding: 30rpx;
border-bottom: 1rpx solid #eee;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.popup-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.popup-close {
font-size: 40rpx;
color: #999;
cursor: pointer;
padding: 10rpx;
}
.popup-body {
flex: 1;
overflow: hidden;
position: relative;
height: 0;
}
.terms-content {
padding: 20rpx;
line-height: 1.8;
font-size: 28rpx;
color: #333;
word-wrap: break-word;
word-break: break-all;
}
.popup-footer {
padding: 30rpx;
border-top: 1rpx solid #eee;
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
}
.popup-tip {
font-size: 24rpx;
color: #999;
margin-bottom: 20rpx;
text-align: center;
}
.popup-btn {
width: 200rpx;
height: 70rpx;
background: #f15a04;
border-radius: 35rpx;
font-size: 28rpx;
color: #ffffff;
border: none;
}
</style>