Files
uni-fans-score/subPackages/user/login/index.vue
T
2026-02-06 18:09:23 +08:00

276 lines
6.3 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="goToPhoneLogin" v-if="isHTML5">
{{ $t('auth.phoneLogin') }}
</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('/subPackages/other/legal/agreement')">{{ $t('user.userAgreement') }}</text>
{{ $t('common.and') }}
<text class="link" @tap.stop="go('/subPackages/other/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 } = useI18n()
// 设置页面标题
onMounted(() => {
uni.setNavigationBarTitle({
title: t('auth.loginTitle')
})
})
const isHTML5 = ref(false) // 是否是HTML5模式
const redirect = ref('/pages/index/index')
const isAgreed = ref(false) // 是否同意协议
// 勾选协议变化
const onAgreementChange = (e) => {
isAgreed.value = e.detail.value.includes('agreed')
}
// 未勾选协议时点击登录按钮
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(t('auth.pleaseAgreeToTerms')))
}
}
})
})
}
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 !== t('auth.pleaseAgreeToTerms')) {
uni.showToast({ title: error.message || t('auth.loginFailed'), 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 (_) {}
}
// #ifdef H5
isHTML5.value = true
// #endif
})
const go = (url) => {
uni.navigateTo({ url })
}
// 跳转到手机号登录页面
const goToPhoneLogin = () => {
uni.navigateTo({ url: '/subPackages/user/login/phone' })
}
</script>
<style lang="scss" scoped>
.login-container {
min-height: 100vh;
background: linear-gradient(180deg, #C8F4D9 0%, #FFFFFF 100%);
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>