255 lines
5.8 KiB
Vue
255 lines
5.8 KiB
Vue
<template>
|
||
<view class="login-container">
|
||
<view class="logo">
|
||
<image src="/static/logo.png" mode="aspectFit" />
|
||
<text class="app-name">风电者共享风扇&充电宝</text>
|
||
</view>
|
||
|
||
<view class="title">登录您的账号</view>
|
||
<view class="subtitle">为保障使用体验,请先完成登录</view>
|
||
|
||
<!-- 微信一键手机号快捷登录(推荐) -->
|
||
<button v-if="!isAgreed" class="btn primary" @click="handleLoginClick">
|
||
手机号快捷登录
|
||
</button>
|
||
<button v-else class="btn primary" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
|
||
手机号快捷登录
|
||
</button>
|
||
|
||
<!-- 仅微信登录(不授权手机号时使用) -->
|
||
<!-- <button class="btn outline" @click="onWeChatLogin">仅微信登录</button> -->
|
||
|
||
<view class="agreement-box">
|
||
<checkbox-group @change="onAgreementChange">
|
||
<label class="agreement-label">
|
||
<checkbox value="agreed" :checked="isAgreed" color="#07c160" class="agreement-checkbox" />
|
||
<text class="agreement-text">
|
||
我已阅读并同意
|
||
<text class="link" @tap.stop="go('/pages/legal/agreement')">《用户协议》</text>
|
||
和
|
||
<text class="link" @tap.stop="go('/pages/legal/privacy')">《隐私政策》</text>
|
||
</text>
|
||
</label>
|
||
</checkbox-group>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
import { wxLogin, getUserPhoneNumber, getUserInfo } from '../../util/index.js'
|
||
|
||
const redirect = ref('/pages/index/index')
|
||
const isAgreed = ref(false) // 是否同意协议
|
||
|
||
// 勾选协议变化
|
||
const onAgreementChange = (e) => {
|
||
isAgreed.value = e.detail.value.includes('agreed')
|
||
console.log('协议勾选状态:', isAgreed.value, e.detail.value)
|
||
}
|
||
|
||
// 未勾选协议时点击登录按钮
|
||
const handleLoginClick = async () => {
|
||
try {
|
||
await checkAgreement()
|
||
// 协议已同意后,按钮会自动切换为带open-type的版本
|
||
} catch (error) {
|
||
// 用户取消了协议同意
|
||
}
|
||
}
|
||
|
||
// 检查是否同意协议
|
||
const checkAgreement = () => {
|
||
return new Promise((resolve, reject) => {
|
||
if (isAgreed.value) {
|
||
resolve()
|
||
return
|
||
}
|
||
|
||
// 未勾选,弹窗提示
|
||
uni.showModal({
|
||
title: '温馨提示',
|
||
content: '请先阅读并同意《用户协议》和《隐私政策》',
|
||
confirmText: '同意',
|
||
cancelText: '取消',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 用户点击同意,自动勾选
|
||
isAgreed.value = true
|
||
resolve()
|
||
} else {
|
||
// 用户点击取消
|
||
reject(new Error('需要同意协议才能登录'))
|
||
}
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
const navigateAfterLogin = async () => {
|
||
try {
|
||
// 可选:刷新一次用户信息
|
||
await getUserInfo().catch(() => {})
|
||
} catch (e) {}
|
||
|
||
// 读取跳转路径(支持 tabBar 页面)
|
||
const target = '/pages/index/index'
|
||
const tabPages = ['/pages/index/index', '/pages/my/index']
|
||
if (tabPages.includes(target)) {
|
||
uni.reLaunch({ url: target })
|
||
return
|
||
}
|
||
uni.reLaunch({ url: target })
|
||
}
|
||
|
||
const onWeChatLogin = async () => {
|
||
try {
|
||
// 先检查是否同意协议
|
||
await checkAgreement()
|
||
|
||
await wxLogin()
|
||
uni.showToast({ title: '登录成功', icon: 'success' })
|
||
await navigateAfterLogin()
|
||
} catch (error) {
|
||
if (error.message !== '需要同意协议才能登录') {
|
||
uni.showToast({ title: error.message || '登录失败', icon: 'none' })
|
||
}
|
||
}
|
||
}
|
||
|
||
const onGetPhoneNumber = async (e) => {
|
||
if (!e || e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||
uni.showToast({ title: '已取消手机号授权', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
try {
|
||
// 先微信登录,获取 token
|
||
await wxLogin()
|
||
// 再用微信返回的临时 code 换取手机号
|
||
await getUserPhoneNumber(e.detail.code)
|
||
uni.showToast({ title: '登录成功', icon: 'success' })
|
||
await navigateAfterLogin()
|
||
} catch (error) {
|
||
uni.showToast({ title: error.message || '登录失败', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
onLoad((opts) => {
|
||
if (opts && opts.redirect) {
|
||
try {
|
||
redirect.value = decodeURIComponent(opts.redirect)
|
||
} catch (_) {}
|
||
}
|
||
})
|
||
|
||
const go = (url) => {
|
||
uni.navigateTo({ url })
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.login-container {
|
||
min-height: 100vh;
|
||
background: #f8f8f8;
|
||
padding: 80rpx 40rpx 40rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
box-sizing: border-box;
|
||
|
||
.logo {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-bottom: 60rpx;
|
||
|
||
image {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.app-name {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.title {
|
||
font-size: 40rpx;
|
||
font-weight: 600;
|
||
color: #222;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 26rpx;
|
||
color: #888;
|
||
margin-bottom: 60rpx;
|
||
}
|
||
|
||
.btn {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
border-radius: 48rpx;
|
||
font-size: 32rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.primary {
|
||
background: #07c160;
|
||
color: #fff;
|
||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.3);
|
||
}
|
||
|
||
.outline {
|
||
background: #fff;
|
||
color: #07c160;
|
||
border: 2rpx solid #07c160;
|
||
}
|
||
|
||
.agreement-box {
|
||
margin-top: 32rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 100%;
|
||
bottom: 40rpx;
|
||
position: absolute;
|
||
|
||
.agreement-label {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
|
||
.agreement-checkbox {
|
||
flex-shrink: 0;
|
||
// margin-right: 12rpx;
|
||
// margin-top: 2rpx;
|
||
transform:scale(0.7);
|
||
}
|
||
|
||
.agreement-text {
|
||
flex: 1;
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
line-height: 1.8;
|
||
word-break: break-all;
|
||
|
||
.link {
|
||
color: #07c160;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|