Files
uni-fans-score/pages/login/index.vue
T

265 lines
6.0 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 class="logo">
<image src="/static/logo.png" mode="aspectFit" />
<text class="app-name">{{ $t('app.slogan') }}</text>
</view>
<view class="title">{{ $t('auth.loginTitle') }}</view>
<view class="subtitle">{{ $t('auth.loginDesc') }}</view>
<!-- 微信一键手机号快捷登录推荐 -->
<button v-if="!isAgreed" class="btn primary" @click="handleLoginClick">
{{ $t('auth.getPhoneNumber') }}
</button>
<button v-else class="btn primary" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
{{ $t('auth.getPhoneNumber') }}
</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">
{{ $t('auth.agreeToTerms') }}
<text class="link" @tap.stop="go('/pages/legal/agreement')">{{ $t('user.userAgreement') }}</text>
{{ $t('common.and') }}
<text class="link" @tap.stop="go('/pages/legal/privacy')">{{ $t('user.privacyPolicy') }}</text>
</text>
</label>
</checkbox-group>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { wxLogin, getUserPhoneNumber, getUserInfo } from '../../util/index.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 设置页面标题
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('auth.loginTitle')
})
})
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: $t('common.tips'),
content: $t('auth.pleaseAgreeToTerms'),
confirmText: $t('common.confirm'),
cancelText: $t('common.cancel'),
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: $t('auth.loginSuccess'), 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: $t('auth.phoneCancelled'), icon: 'none' })
return
}
try {
// 先微信登录,获取 token
await wxLogin()
// 再用微信返回的临时 code 换取手机号
await getUserPhoneNumber(e.detail.code)
uni.showToast({ title: $t('auth.loginSuccess'), icon: 'success' })
await navigateAfterLogin()
} catch (error) {
uni.showToast({ title: error.message || $t('auth.loginFailed'), 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>