topfans/frontend/pages/login/quickLogin.vue

619 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>
<LoginBackground :show-logo="true" :showBack="false">
<view class="quicklogin-content">
<view class="quicklogin-card">
<!-- 标题 -->
<view class="card-title-wrapper">
<text class="card-title">欢迎回来</text>
</view>
<!-- 头像 -->
<view class="avatar-wrapper">
<view class="avatar-select-box">
<image
class="avatar-select-bg"
src="/static/login/portal/setnickname-avatar-box.png"
mode="aspectFit"
></image>
<view class="avatar-select-inner">
<image
v-if="userAvatar"
class="avatar-inner-img"
:src="userAvatar"
mode="aspectFill"
></image>
</view>
</view>
</view>
<!-- 手机号 -->
<view class="phone-row">
<text class="phone-text">{{ maskedPhone }}</text>
</view>
<!-- 分割线 -->
<view class="divider"></view>
<!-- 一键登录按钮 -->
<view class="primary-btn-wrapper">
<view class="primary-btn" @tap="handleQuickLogin">
<text class="primary-btn-text">一键登录</text>
</view>
</view>
<!-- 其他账号登录 -->
<view class="secondary-btn-wrapper">
<view class="secondary-btn" @tap="handleOtherLogin">
<text class="secondary-btn-text">其他账号登录</text>
</view>
</view>
<!-- 底部协议 -->
<view class="agreement-wrapper">
<view
class="agreement-checkbox"
:class="{ checked: agreedToTerms }"
@tap="toggleAgreement"
>
<view v-if="agreedToTerms" class="checkbox-inner"></view>
</view>
<view class="agreement-text">
<text class="agreement-line"
>登陆代表您已阅读并接受统一认证服务条款,以及</text
>
<text
class="agreement-line agreement-link"
@tap="showAgreementModal"
>隐私政策和用户服务协议</text
>
</view>
</view>
</view>
</view>
<!-- 提示弹窗 -->
<view v-if="showTipDialog" class="tip-dialog-mask" @tap="closeTipDialog">
<view class="tip-dialog" @tap.stop>
<view class="tip-dialog-header">
<text class="tip-dialog-title">提示</text>
<text class="tip-dialog-close" @tap="closeTipDialog">×</text>
</view>
<view class="tip-dialog-content">
<text class="tip-text">阅读并同意以下条款</text>
<text class="tip-agreement-link" @tap="openAgreementFromTip"
>《Topfans用户服务协议》</text
>
</view>
</view>
</view>
<!-- 协议全文弹窗 -->
<view
v-if="showAgreementDialog"
class="agreement-dialog-mask"
@tap="closeAgreementDialog"
>
<view class="agreement-dialog" @tap.stop>
<view class="agreement-dialog-header">
<text class="agreement-dialog-title">Topfans用户服务协议</text>
<text class="agreement-dialog-close" @tap="closeAgreementDialog"
>×</text
>
</view>
<scroll-view class="agreement-dialog-content" scroll-y>
<text class="agreement-dialog-text">{{
agreementContent || "协议内容加载中..."
}}</text>
</scroll-view>
<view class="agreement-dialog-footer">
<button class="agreement-confirm-btn" @tap="agreeAndClose">
我同意
</button>
</view>
</view>
</view>
</LoginBackground>
</template>
<script setup>
import { ref } from "vue";
import LoginBackground from "@/components/LoginBackground.vue";
import { AGREEMENT_CONTENT } from "@/utils/agreement";
// 用户信息(从本地缓存读取 user 信息)
const userInfo = JSON.parse(uni.getStorageSync("user")) || {};
// 本地脱敏兜底:服务端若返回原始手机号,前端做脱敏
const maskPhone = (phone) => {
if (!phone) return "";
const s = String(phone);
if (s.length < 7) return s;
if (s.includes("*")) return s; // 已是脱敏值
return `${s.slice(0, 3)}****${s.slice(-4)}`;
};
const maskedPhone = ref(
userInfo.mobile_masked || maskPhone(userInfo.mobile) || "174****2223",
);
const userAvatar = ref(
userInfo.avatar_url || "/static/login/portal/quicklogin-avatar.png",
);
const agreedToTerms = ref(false);
// 协议弹窗相关
const agreementContent = ref("");
const showAgreementDialog = ref(false);
const showTipDialog = ref(false);
const toggleAgreement = () => {
agreedToTerms.value = !agreedToTerms.value;
};
// 一键登录
const handleQuickLogin = () => {
if (!agreedToTerms.value) {
showTipDialog.value = true;
return;
}
uni.showLoading({ title: "登录中..." });
setTimeout(() => {
uni.hideLoading();
// 标记从一键登录进入,触发 TopfansWelcome 显示
uni.setStorageSync("needs_welcome", true);
uni.reLaunch({ url: "/pages/square/square" });
}, 800);
};
// 其他账号登录
const handleOtherLogin = () => {
uni.reLaunch({ url: "/pages/login/portal" });
};
// 显示协议全文弹窗
const showAgreementModal = () => {
if (!agreementContent.value) {
agreementContent.value = AGREEMENT_CONTENT;
}
showAgreementDialog.value = true;
};
// 关闭协议弹窗
const closeAgreementDialog = () => {
showAgreementDialog.value = false;
};
// 同意并关闭弹窗
const agreeAndClose = () => {
agreedToTerms.value = true;
showAgreementDialog.value = false;
};
// 关闭提示弹窗
const closeTipDialog = () => {
showTipDialog.value = false;
};
// 从提示弹窗打开协议弹窗
const openAgreementFromTip = () => {
showTipDialog.value = false;
setTimeout(() => {
showAgreementModal();
}, 300);
};
</script>
<style scoped>
.quicklogin-content {
position: absolute;
z-index: 10;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
top: 160rpx;
}
/* 卡片 */
.quicklogin-card {
width: 710rpx;
padding: 30rpx 40rpx 50rpx;
box-sizing: border-box;
border-radius: 56rpx;
border: 2rpx solid rgba(255, 255, 255, 0.15);
background: rgba(241, 163, 163, 0.17);
backdrop-filter: blur(64rpx);
-webkit-backdrop-filter: blur(64rpx);
box-shadow: 0 0 41rpx 0 rgba(0, 0, 0, 0.31);
display: flex;
flex-direction: column;
align-items: center;
}
/* 标题 */
.card-title-wrapper {
width: 270rpx;
height: 84rpx;
display: flex;
align-items: center;
justify-content: center;
background:
linear-gradient(240deg, #cdaffa 9.77%, rgba(205, 175, 250, 0) 62.44%),
linear-gradient(108deg, #f7cbbf 13.91%, rgba(247, 203, 191, 0) 51.34%),
linear-gradient(297deg, #81a9f8 31.28%, rgba(129, 169, 248, 0) 90.74%),
linear-gradient(80deg, #69f4e9 39.86%, rgba(164, 252, 245, 0) 66.88%);
border-radius: 84rpx;
margin-top: 88rpx;
margin-bottom: 30rpx;
}
.card-title {
font-size: 40rpx;
color: #fff9e7;
text-shadow: -2rpx 2rpx 8rpx rgba(0, 0, 0, 0.84);
font-family: "Abhaya Libre Medium", sans-serif;
}
/* 头像 */
.avatar-wrapper {
position: relative;
width: 270rpx;
height: 270rpx;
display: flex;
align-items: center;
justify-content: center;
margin: 48rpx 0;
}
.avatar-select-box {
position: relative;
width: 270rpx;
height: 270rpx;
}
.avatar-select-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
filter: drop-shadow(0 0 23rpx rgba(135, 16, 16, 0.67));
}
.avatar-select-inner {
position: absolute;
top: 18rpx;
left: 18rpx;
right: 18rpx;
bottom: 18rpx;
border-radius: 50%;
background: linear-gradient(
147deg,
rgba(223, 223, 146, 0.87) 17%,
rgba(135, 197, 216, 0.87) 39%,
rgba(194, 123, 209, 0.87) 99%
);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.avatar-inner-img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
}
/* 手机号 */
.phone-row {
margin-bottom: 56rpx;
}
.phone-text {
font-size: 60rpx;
color: #fff9e7;
text-shadow: -2rpx 2rpx 8rpx rgba(0, 0, 0, 0.84);
font-family: "Acme", sans-serif;
letter-spacing: 4rpx;
}
/* 分割线 */
.divider {
width: 584rpx;
height: 2rpx;
background: rgba(255, 228, 228, 0.46);
border-radius: 20rpx;
margin: 0 0 62rpx;
}
/* 一键登录按钮 */
.primary-btn-wrapper {
width: 100%;
display: flex;
justify-content: center;
margin-bottom: 32rpx;
}
.primary-btn {
width: 394rpx;
height: 104rpx;
border-radius: 58rpx;
background: linear-gradient(
90deg,
rgba(255, 222, 8, 0.41) 0%,
rgba(252, 100, 102, 0.83) 64%,
rgba(244, 88, 104, 0.83) 100%
);
box-shadow: 4rpx 4rpx 8rpx 0 rgba(242, 21, 21, 0.47);
display: flex;
align-items: center;
justify-content: center;
opacity: 0.96;
}
.primary-btn-text {
font-size: 42rpx;
color: #fff9e7;
text-shadow: -2rpx 2rpx 8rpx rgba(0, 0, 0, 0.84);
font-family: "Abhaya Libre Medium", sans-serif;
}
/* 其他账号登录 */
.secondary-btn-wrapper {
width: 100%;
display: flex;
justify-content: center;
}
.secondary-btn {
width: 394rpx;
height: 104rpx;
border-radius: 58rpx;
background: linear-gradient(
90deg,
rgba(255, 222, 8, 0.06) 0%,
rgba(252, 100, 102, 0.12) 64%,
rgba(244, 88, 104, 0.12) 100%
);
box-shadow: 4rpx 4rpx 8rpx 0 rgba(242, 21, 21, 0.47);
display: flex;
align-items: center;
justify-content: center;
opacity: 0.96;
}
.secondary-btn-text {
font-size: 42rpx;
color: #fff9e7;
text-shadow: -2rpx 2rpx 8rpx rgba(0, 0, 0, 0.84);
font-family: "Abhaya Libre Medium", sans-serif;
}
/* 底部协议 */
.agreement-wrapper {
margin-top: 200rpx;
width: 100%;
display: flex;
align-items: center;
padding: 0 8rpx;
}
.agreement-checkbox {
width: 44rpx;
height: 44rpx;
border-radius: 58rpx;
background: rgba(59, 58, 56, 0.19);
box-shadow: 4rpx 4rpx 18rpx 0 rgba(242, 21, 21, 0.27);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.96;
}
.checkbox-inner {
width: 30rpx;
height: 30rpx;
border-radius: 50%;
background: #ffffff;
}
.agreement-text {
margin-left: 12rpx;
display: flex;
flex-direction: column;
}
.agreement-line {
font-size: 24rpx;
color: #fff;
text-shadow: 0 2rpx 13rpx rgba(0, 0, 0, 1);
font-family: "Abhaya Libre Medium", sans-serif;
line-height: 1.4;
}
.agreement-link {
text-decoration: underline;
cursor: pointer;
}
/* 提示弹窗样式 */
.tip-dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 9998;
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
}
.tip-dialog {
width: 100%;
max-width: 600rpx;
background: #e6e6e6;
border-radius: 20rpx;
display: flex;
flex-direction: column;
overflow: hidden;
}
.tip-dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 40rpx;
border-bottom: 1rpx solid #e5e5e5;
position: relative;
}
.tip-dialog-title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
flex: 1;
text-align: center;
}
.tip-dialog-close {
font-size: 48rpx;
color: #999999;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 20rpx;
top: 50%;
transform: translateY(-50%);
}
.tip-dialog-content {
padding: 40rpx;
text-align: center;
}
.tip-text {
font-size: 32rpx;
color: #333333;
line-height: 1.6;
display: block;
margin-bottom: 20rpx;
}
.tip-agreement-link {
font-size: 32rpx;
color: #007aff;
text-decoration: underline;
cursor: pointer;
display: block;
}
/* 协议全文弹窗 */
.agreement-dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
}
.agreement-dialog {
width: 100%;
max-width: 700rpx;
max-height: 80vh;
background: #e6e6e6;
border-radius: 20rpx;
display: flex;
flex-direction: column;
overflow: hidden;
}
.agreement-dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 40rpx;
border-bottom: 1rpx solid #e5e5e5;
position: relative;
}
.agreement-dialog-title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
flex: 1;
text-align: center;
}
.agreement-dialog-close {
font-size: 48rpx;
color: #999999;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 20rpx;
top: 50%;
transform: translateY(-50%);
}
.agreement-dialog-content {
flex: 1;
padding: 40rpx;
overflow-y: auto;
box-sizing: border-box;
width: 100%;
}
.agreement-dialog-text {
font-size: 28rpx;
color: #333333;
line-height: 1.8;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
overflow-wrap: break-word;
width: 100%;
box-sizing: border-box;
display: block;
}
.agreement-dialog-footer {
padding: 30rpx 40rpx;
border-top: 1rpx solid #e5e5e5;
}
.agreement-confirm-btn {
width: 100%;
height: 88rpx;
background: #000000;
color: #e6e6e6;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: bold;
border: none;
display: flex;
align-items: center;
justify-content: center;
}
.agreement-confirm-btn::after {
border: none;
}
</style>