551 lines
10 KiB
Vue
551 lines
10 KiB
Vue
<template>
|
||
<view class="login-container">
|
||
<!-- 背景装饰 -->
|
||
<view class="bg-decoration">
|
||
<!-- 云朵装饰 -->
|
||
<view class="cloud cloud-1"></view>
|
||
<view class="cloud cloud-2"></view>
|
||
<view class="cloud cloud-3"></view>
|
||
<!-- 小鸟装饰 -->
|
||
<view class="bird bird-1"></view>
|
||
<view class="bird bird-2"></view>
|
||
</view>
|
||
|
||
<!-- 头部区域 -->
|
||
<view class="header">
|
||
<image src="@@:app/static/Logo.jpg" class="logo" mode="aspectFit"></image>
|
||
<text class="welcome-text">欢迎登录</text>
|
||
</view>
|
||
|
||
<!-- 登录表单 -->
|
||
<view class="login-form">
|
||
<!-- 一键登录按钮 -->
|
||
<view class="one-click-login">
|
||
<button v-if="agreedToTerms && isMobile" class="quick-login-btn" :class="{ disabled: loading }"
|
||
open-type="getPhoneNumber" @getphonenumber="mobileClickLogin">
|
||
<text>{{ loading ? '登录中...' : '一键登录' }}</text>
|
||
</button>
|
||
<button v-if="!agreedToTerms" class="quick-login-btn" :class="{ disabled: loading }"
|
||
@click="agreementClickLogin">
|
||
<text>{{ loading ? '登录中...' : '一键登录' }}</text>
|
||
</button>
|
||
<button v-if="agreedToTerms && !isMobile" class="quick-login-btn" :class="{ disabled: loading }"
|
||
@click="anonymousLoginClickLogin">
|
||
<text>{{ loading ? '登录中...' : '一键登录' }}</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 用户协议同意 -->
|
||
<view class="agreement-section">
|
||
<view class="agreement-checkbox" @click="toggleAgreement">
|
||
|
||
<checkbox :value="agreedToTerms" :checked="agreedToTerms"
|
||
style="transform:scale(0.5);margin-top: -8rpx;" />
|
||
<text class="agreement-text">
|
||
我已阅读并同意
|
||
<text class="agreement-link" @click.stop="showUserAgreement">《用户协议》</text>
|
||
和
|
||
<text class="agreement-link" @click.stop="showPrivacyPolicy">《隐私政策》</text>
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 暂不登录按钮 -->
|
||
<view class="skip-login-btn" @click="skipLogin">
|
||
<text>暂不登录</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
reactive,
|
||
computed
|
||
} from 'vue'
|
||
import { useLogin } from '@/common/com'
|
||
import { getAnonymousLogin, ueWxPhoneNumberLogin, useWxAnonymousLogin } from '@/common/server/interface/user'
|
||
import { userInfo, loadUserInfo } from '@/common/server/user'
|
||
// 响应式数据
|
||
const loading = ref(false)
|
||
const agreedToTerms = ref(false)
|
||
|
||
// 方法
|
||
const toggleAgreement = () => {
|
||
agreedToTerms.value = !agreedToTerms.value
|
||
}
|
||
|
||
const agreementClickLogin = async () => {
|
||
if (loading.value) return
|
||
|
||
if (!agreedToTerms.value) {
|
||
uni.showToast({
|
||
title: '请先同意用户协议和隐私政策',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
}
|
||
const mobileClickLogin = async (e) => {
|
||
console.log(e) // 动态令牌
|
||
console.log(e.detail.code) // 动态令牌
|
||
var code = e.detail.code;
|
||
loading.value = true
|
||
try {
|
||
// 这里调用一键登录API(如运营商一键登录)
|
||
// const response = await oneClickLoginApi()
|
||
var response = await ueWxPhoneNumberLogin(code, sessionAuthId);
|
||
if (response == null) {
|
||
uni.showToast({
|
||
title: '登录失败,请重试',
|
||
icon: 'error'
|
||
})
|
||
return;
|
||
}
|
||
uni.setStorageSync("tokenInfo", response);
|
||
|
||
await loadUserInfo();
|
||
uni.showToast({
|
||
title: '登录成功',
|
||
icon: 'success'
|
||
});
|
||
// 延迟跳转
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
|
||
} catch (error) {
|
||
console.error('一键登录失败:', error)
|
||
uni.showToast({
|
||
title: '登录失败,请重试',
|
||
icon: 'error'
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
const anonymousLoginClickLogin = async (e) => {
|
||
console.log('aaa');
|
||
|
||
loading.value = true
|
||
try {
|
||
// 这里调用一键登录API(如运营商一键登录)
|
||
// const response = await oneClickLoginApi()
|
||
var response = await useWxAnonymousLogin(sessionAuthId);
|
||
if (response == null) {
|
||
uni.showToast({
|
||
title: '登录失败,请重试',
|
||
icon: 'error'
|
||
})
|
||
return;
|
||
}
|
||
uni.setStorageSync("tokenInfo", response);
|
||
await loadUserInfo();
|
||
uni.showToast({
|
||
title: '登录成功',
|
||
icon: 'success'
|
||
});
|
||
// 延迟跳转
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
|
||
} catch (error) {
|
||
console.error('一键登录失败:', error)
|
||
uni.showToast({
|
||
title: '登录失败,请重试',
|
||
icon: 'error'
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
const skipLogin = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
const showUserAgreement = () => {
|
||
com.navigateToAgreement('userAgreement');
|
||
|
||
}
|
||
|
||
const showPrivacyPolicy = () => {
|
||
com.navigateToAgreement('privacyPolicy');
|
||
}
|
||
let isMobile = ref(false);
|
||
let sessionAuthId = null;
|
||
async function AnonymousLogin() {
|
||
var code = await useLogin();
|
||
console.log("res", code);
|
||
var res = await getAnonymousLogin(code);
|
||
if (res.user == 0) {
|
||
isMobile.value = true;
|
||
}
|
||
sessionAuthId = res.sessionAuthId;
|
||
}
|
||
onLoad(() => {
|
||
AnonymousLogin();
|
||
/* uni.login({
|
||
|
||
}) */
|
||
})
|
||
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.login-container {
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #87CEEB 0%, #98FB98 50%, #F0E68C 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.bg-decoration {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
}
|
||
|
||
// 云朵样式
|
||
.cloud {
|
||
position: absolute;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
border-radius: 50rpx;
|
||
|
||
&::before,
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
border-radius: 50%;
|
||
}
|
||
|
||
&.cloud-1 {
|
||
width: 120rpx;
|
||
height: 40rpx;
|
||
top: 15%;
|
||
left: 10%;
|
||
|
||
&::before {
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
top: -25rpx;
|
||
left: 10rpx;
|
||
}
|
||
|
||
&::after {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
top: -30rpx;
|
||
right: 10rpx;
|
||
}
|
||
}
|
||
|
||
&.cloud-2 {
|
||
width: 100rpx;
|
||
height: 35rpx;
|
||
top: 25%;
|
||
right: 15%;
|
||
|
||
&::before {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
top: -20rpx;
|
||
left: 8rpx;
|
||
}
|
||
|
||
&::after {
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
top: -25rpx;
|
||
right: 8rpx;
|
||
}
|
||
}
|
||
|
||
&.cloud-3 {
|
||
width: 80rpx;
|
||
height: 30rpx;
|
||
top: 70%;
|
||
left: 20%;
|
||
|
||
&::before {
|
||
width: 35rpx;
|
||
height: 35rpx;
|
||
top: -17rpx;
|
||
left: 6rpx;
|
||
}
|
||
|
||
&::after {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
top: -20rpx;
|
||
right: 6rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 小鸟样式
|
||
.bird {
|
||
position: absolute;
|
||
width: 20rpx;
|
||
height: 15rpx;
|
||
background: #8B4513;
|
||
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
width: 8rpx;
|
||
height: 6rpx;
|
||
background: #8B4513;
|
||
border-radius: 50%;
|
||
top: 2rpx;
|
||
left: -5rpx;
|
||
transform: rotate(-20deg);
|
||
}
|
||
|
||
&.bird-1 {
|
||
top: 20%;
|
||
right: 25%;
|
||
animation: fly 3s ease-in-out infinite;
|
||
}
|
||
|
||
&.bird-2 {
|
||
top: 60%;
|
||
left: 15%;
|
||
animation: fly 4s ease-in-out infinite reverse;
|
||
}
|
||
}
|
||
|
||
// 花朵样式
|
||
.flower {
|
||
position: absolute;
|
||
width: 30rpx;
|
||
height: 30rpx;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: radial-gradient(circle, #FFB6C1 30%, #FF69B4 70%);
|
||
border-radius: 50%;
|
||
}
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
width: 6rpx;
|
||
height: 20rpx;
|
||
background: #228B22;
|
||
border-radius: 3rpx;
|
||
bottom: -20rpx;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
}
|
||
|
||
&.flower-1 {
|
||
top: 40%;
|
||
left: 8%;
|
||
animation: sway 2s ease-in-out infinite;
|
||
}
|
||
|
||
&.flower-2 {
|
||
top: 80%;
|
||
right: 10%;
|
||
animation: sway 2.5s ease-in-out infinite reverse;
|
||
}
|
||
|
||
&.flower-3 {
|
||
top: 50%;
|
||
right: 30%;
|
||
animation: sway 3s ease-in-out infinite;
|
||
}
|
||
}
|
||
|
||
// 动画效果
|
||
@keyframes fly {
|
||
|
||
0%,
|
||
100% {
|
||
transform: translateX(0) translateY(0);
|
||
}
|
||
|
||
25% {
|
||
transform: translateX(10rpx) translateY(-5rpx);
|
||
}
|
||
|
||
50% {
|
||
transform: translateX(20rpx) translateY(0);
|
||
}
|
||
|
||
75% {
|
||
transform: translateX(10rpx) translateY(5rpx);
|
||
}
|
||
}
|
||
|
||
@keyframes sway {
|
||
|
||
0%,
|
||
100% {
|
||
transform: rotate(0deg);
|
||
}
|
||
|
||
25% {
|
||
transform: rotate(2deg);
|
||
}
|
||
|
||
50% {
|
||
transform: rotate(0deg);
|
||
}
|
||
|
||
75% {
|
||
transform: rotate(-2deg);
|
||
}
|
||
}
|
||
|
||
@keyframes shine {
|
||
0% {
|
||
transform: translateX(-100%) translateY(-100%) rotate(45deg);
|
||
}
|
||
|
||
100% {
|
||
transform: translateX(100%) translateY(100%) rotate(45deg);
|
||
}
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 80rpx;
|
||
|
||
.logo {
|
||
width: 200rpx;
|
||
height: 200rpx;
|
||
border-radius: 50%;
|
||
margin-bottom: 30rpx;
|
||
border: 8rpx solid #FFE4B5;
|
||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.app-name {
|
||
display: block;
|
||
font-size: 48rpx;
|
||
font-weight: bold;
|
||
color: #8B4513;
|
||
margin-bottom: 20rpx;
|
||
text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.welcome-text {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #228B22;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.login-form {
|
||
padding: 0 60rpx;
|
||
width: 100%;
|
||
max-width: 600rpx;
|
||
}
|
||
|
||
.one-click-login {
|
||
margin: 40rpx 0;
|
||
|
||
.quick-login-btn {
|
||
background: linear-gradient(135deg, #F36903 0%, #E55A00 100%);
|
||
color: white;
|
||
height: 100rpx;
|
||
border-radius: 50rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
box-shadow: 0 8rpx 20rpx rgba(243, 105, 3, 0.3);
|
||
border: 4rpx solid #FFE4B5;
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
left: -50%;
|
||
width: 200%;
|
||
height: 200%;
|
||
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||
transform: rotate(45deg);
|
||
transition: all 0.5s;
|
||
}
|
||
|
||
&:active::before {
|
||
animation: shine 0.6s ease-in-out;
|
||
}
|
||
|
||
&.disabled {
|
||
background: linear-gradient(135deg, #D3D3D3 0%, #A9A9A9 100%);
|
||
color: #666;
|
||
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.quick-login-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
margin-right: 15rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.agreement-section {
|
||
margin: 30rpx 0;
|
||
|
||
.agreement-checkbox {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
|
||
.checkbox {
|
||
width: 30rpx;
|
||
height: 30rpx;
|
||
margin-right: 15rpx;
|
||
margin-top: 5rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.agreement-text {
|
||
font-size: 24rpx;
|
||
color: #8B4513;
|
||
line-height: 1.5;
|
||
|
||
.agreement-link {
|
||
color: blue;
|
||
text-decoration: underline;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.skip-login-btn {
|
||
text-align: center;
|
||
margin: 40rpx 0;
|
||
|
||
text {
|
||
font-size: 28rpx;
|
||
color: #8B4513;
|
||
text-decoration: underline;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
</style> |