622 lines
14 KiB
Vue
622 lines
14 KiB
Vue
<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|| userInfo.mobile || 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);
|
||
// 原因:quickLogin 只在 App 冷启动 + 有 token 时由 App.vue 跳转进入,
|
||
// 若写入 needs_welcome 则每次系统杀掉进程重启都会触发 TopfansWelcome,
|
||
// 用户体验很差。needs_welcome 由 login.vue(密码登录)和 selectRole.vue(注册选角)负责写入。
|
||
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>
|