600 行
16 KiB
Vue
600 行
16 KiB
Vue
<template>
|
||
<view class="auth-login-page" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
|
||
<!-- 顶部品牌视觉与应用 Slogan -->
|
||
<view class="brand-header">
|
||
<view class="brand-logo-wrap">
|
||
<image class="brand-logo-img" :src="APP_LOGO" mode="aspectFit" />
|
||
</view>
|
||
<text class="brand-app-name">同城陪玩生活圈</text>
|
||
<text class="brand-slogan">年轻人的同城搭子与温暖陪伴社交平台</text>
|
||
</view>
|
||
<!-- 微信手机号授权快捷登录主操作区 (微信官方推荐格式) -->
|
||
<view class="main-action-section">
|
||
<button
|
||
class="btn-wechat-primary btn-active"
|
||
open-type="getPhoneNumber"
|
||
@getphonenumber="handleGetPhoneNumber"
|
||
@click="handleFallbackLogin"
|
||
>
|
||
<image class="wechat-icon-svg" :src="ICONS.shield" mode="aspectFit" />
|
||
<text class="btn-text">一键授权快捷登录</text>
|
||
</button>
|
||
<text class="auth-guarantee-tip">微信官方安全风控 · 个人信息严格加密存储</text>
|
||
</view>
|
||
|
||
<!-- 多角色体验账号矩阵 (局长/买家/金牌大神) -->
|
||
<view class="demo-accounts-card">
|
||
<view class="demo-card-head">
|
||
<text class="demo-title">多角色体验账号直登</text>
|
||
<text class="demo-sub">评审与体验专用 · 一键切换角色</text>
|
||
</view>
|
||
|
||
<view class="demo-list">
|
||
<view
|
||
v-for="acc in DEMO_ACCOUNTS"
|
||
:key="acc.id"
|
||
class="demo-item btn-active"
|
||
@click="loginWithDemo(acc)"
|
||
>
|
||
<image class="demo-avatar" :src="acc.avatar" mode="aspectFill" />
|
||
<view class="demo-info">
|
||
<view class="demo-name-row">
|
||
<text class="demo-name">{{ acc.nickname }}</text>
|
||
<text class="demo-badge" :class="acc.roleTag">{{ acc.roleDesc }}</text>
|
||
</view>
|
||
<text class="demo-feature">{{ acc.desc }}</text>
|
||
</view>
|
||
<button class="btn-switch-account btn-active">一键体验</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部合规声明与服务协议勾选 (单行紧凑排列) -->
|
||
<view class="protocol-section" @click="agreedProtocol = !agreedProtocol">
|
||
<view class="checkbox" :class="{ checked: agreedProtocol }">
|
||
<view class="checkbox-inner" v-if="agreedProtocol"></view>
|
||
</view>
|
||
<view class="protocol-text">
|
||
<text>登录注册即同意</text>
|
||
<text class="protocol-link" @click.stop="openUserAgreement">《用户服务协议》</text>
|
||
<text>与</text>
|
||
<text class="protocol-link" @click.stop="openPrivacyAgreement">《隐私保护指引》</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted } from 'vue';
|
||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||
import { ICONS } from '@/common/icons.js';
|
||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||
import { getPlatformConfigs } from '@/common/api.js';
|
||
|
||
const APP_LOGO = 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=200';
|
||
const DEFAULT_AVATAR = 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=200';
|
||
|
||
const currentThemeKey = ref(getCurrentThemeKey());
|
||
const agreedProtocol = ref(true);
|
||
const redirectUrl = ref('');
|
||
|
||
// 动态协议与隐私政策配置 (支持管理后台灵活自定义配置与动态热更新)
|
||
const platformProtocol = ref({
|
||
userAgreementTitle: '用户服务协议',
|
||
userAgreementContent: '1. 账号使用规范:用户注册与使用同城生活陪伴服务须符合国家法律法规,严禁利用平台从事任何违法违规行为。\n2. 资金与交易保障:平台提供全额担保交易,订单完成前款项由官方账户托管,碰面出示4位核销码确认后自动结算至大神钱包。\n3. 履约安全守则:所有同城活动须在合规公开商业街区进行,严禁私下导流与脱离平台交易。\n4. 争议与售后仲裁:若遇服务不符或搭子违约,可随时在订单中发起申诉,由平台运营仲裁专员裁决。',
|
||
privacyPolicyTitle: '隐私保护指引',
|
||
privacyPolicyContent: '1. 信息收集范围:为保障同城陪玩交易安全与现场4位核销码核验,平台在获得您授权后会依法收集手机号与必要位置信息。\n2. 数据加密保护:平台全面接入微信小程序官方安全风控规范,所有用户数据均实施高强度加密脱敏存储。\n3. 权限撤回与管理:您可随时在小程序系统设置中撤回位置或通讯授权,平台未经您的明确许可绝不会向任何第三方泄露您的隐私数据。'
|
||
});
|
||
|
||
const loadProtocolConfig = async () => {
|
||
try {
|
||
const res = await getPlatformConfigs();
|
||
if (res && res.platform_protocol) {
|
||
const p = res.platform_protocol;
|
||
if (typeof p === 'object') {
|
||
if (p.userAgreementTitle) platformProtocol.value.userAgreementTitle = p.userAgreementTitle;
|
||
if (p.userAgreementContent || p.content) {
|
||
platformProtocol.value.userAgreementContent = p.userAgreementContent || p.content;
|
||
}
|
||
if (p.privacyPolicyTitle) platformProtocol.value.privacyPolicyTitle = p.privacyPolicyTitle;
|
||
if (p.privacyPolicyContent) {
|
||
platformProtocol.value.privacyPolicyContent = p.privacyPolicyContent;
|
||
}
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.warn('[Login] 动态拉取后台协议配置降级:', err);
|
||
}
|
||
};
|
||
|
||
const DEMO_ACCOUNTS = [
|
||
{
|
||
id: 1001,
|
||
nickname: '晨曦微光局长',
|
||
roleTag: 'host',
|
||
roleDesc: '房主/局长',
|
||
desc: '余额充足 · 发起项目制/AA组局',
|
||
avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=200'
|
||
},
|
||
{
|
||
id: 2001,
|
||
nickname: '快乐小买家',
|
||
roleTag: 'buyer',
|
||
roleDesc: '普通买家',
|
||
desc: '搭子报名 · 预约陪伴 · 核销码签到',
|
||
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=200'
|
||
},
|
||
{
|
||
id: 101,
|
||
nickname: '糖心小糯米',
|
||
roleTag: 'expert',
|
||
roleDesc: '金牌陪伴师',
|
||
desc: '工作台接单 · 雷达抢单 · 现场核销',
|
||
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=200'
|
||
}
|
||
];
|
||
|
||
onLoad((options) => {
|
||
if (options && options.redirect) {
|
||
try {
|
||
redirectUrl.value = decodeURIComponent(options.redirect);
|
||
} catch (_) {
|
||
redirectUrl.value = options.redirect;
|
||
}
|
||
}
|
||
});
|
||
|
||
onMounted(() => {
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI(currentThemeKey.value);
|
||
loadProtocolConfig();
|
||
});
|
||
|
||
onShow(() => {
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI(currentThemeKey.value);
|
||
loadProtocolConfig();
|
||
});
|
||
|
||
const completeLogin = (userId, customData = {}) => {
|
||
uni.showLoading({ title: '正在登录...' });
|
||
|
||
setTimeout(() => {
|
||
uni.hideLoading();
|
||
uni.removeStorageSync('user_has_logged_out');
|
||
uni.setStorageSync('userId', userId);
|
||
uni.setStorageSync('userInfo', {
|
||
id: userId,
|
||
nickname: customData.nickname || '微信用户',
|
||
avatar: customData.avatar || DEFAULT_AVATAR
|
||
});
|
||
|
||
uni.$emit('login-success', { userId });
|
||
uni.showToast({ title: '登录成功', icon: 'success', duration: 1500 });
|
||
|
||
setTimeout(() => {
|
||
if (redirectUrl.value) {
|
||
if (redirectUrl.value.startsWith('/pages/index') ||
|
||
redirectUrl.value.startsWith('/pages/activity/list') ||
|
||
redirectUrl.value.startsWith('/pages/order/list') ||
|
||
redirectUrl.value.startsWith('/pages/user/index')) {
|
||
uni.switchTab({ url: redirectUrl.value });
|
||
} else {
|
||
uni.redirectTo({ url: redirectUrl.value });
|
||
}
|
||
} else {
|
||
uni.navigateBack({
|
||
fail: () => {
|
||
uni.switchTab({ url: '/pages/user/index' });
|
||
}
|
||
});
|
||
}
|
||
}, 400);
|
||
}, 350);
|
||
};
|
||
|
||
const handleGetPhoneNumber = (e) => {
|
||
if (!agreedProtocol.value) {
|
||
uni.showToast({ title: '请先阅读并勾选服务协议', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
// #ifdef MP-WEIXIN
|
||
if (e && e.detail && (e.detail.code || e.detail.encryptedData)) {
|
||
completeLogin(1001, {
|
||
nickname: '微信用户',
|
||
avatar: DEFAULT_AVATAR
|
||
});
|
||
} else {
|
||
console.warn('[WeChat Login] 未授权手机号或环境受限:', e?.detail?.errMsg);
|
||
uni.showModal({
|
||
title: '登录提示',
|
||
content: '未获得微信手机号授权(需非个人主体认证小程序支持)。是否使用体验账号直接登录?',
|
||
confirmText: '快捷登录',
|
||
cancelText: '取消',
|
||
confirmColor: '#10B981',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
completeLogin(1001);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
// #endif
|
||
|
||
// #ifndef MP-WEIXIN
|
||
completeLogin(1001);
|
||
// #endif
|
||
};
|
||
|
||
const handleFallbackLogin = () => {
|
||
// #ifndef MP-WEIXIN
|
||
if (!agreedProtocol.value) {
|
||
uni.showToast({ title: '请先阅读并勾选服务协议', icon: 'none' });
|
||
return;
|
||
}
|
||
completeLogin(1001);
|
||
// #endif
|
||
};
|
||
|
||
const loginWithDemo = (acc) => {
|
||
if (!agreedProtocol.value) {
|
||
uni.showToast({ title: '请先阅读并勾选服务协议', icon: 'none' });
|
||
return;
|
||
}
|
||
completeLogin(acc.id, {
|
||
nickname: acc.nickname,
|
||
avatar: acc.avatar
|
||
});
|
||
};
|
||
|
||
const openUserAgreement = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/common/protocol?type=service',
|
||
fail: () => {
|
||
uni.showModal({
|
||
title: platformProtocol.value.userAgreementTitle || '用户服务协议',
|
||
content: platformProtocol.value.userAgreementContent,
|
||
showCancel: false,
|
||
confirmText: '我知道了'
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
const openPrivacyAgreement = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/common/protocol?type=privacy',
|
||
fail: () => {
|
||
uni.showModal({
|
||
title: platformProtocol.value.privacyPolicyTitle || '隐私保护指引',
|
||
content: platformProtocol.value.privacyPolicyContent,
|
||
showCancel: false,
|
||
confirmText: '我知道了'
|
||
});
|
||
}
|
||
});
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.auth-login-page {
|
||
min-height: 100vh;
|
||
background-color: #0A0F1D;
|
||
box-sizing: border-box;
|
||
padding: 40rpx 36rpx calc(60rpx + env(safe-area-inset-bottom));
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 顶部品牌区 */
|
||
.brand-header {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-top: 60rpx;
|
||
margin-bottom: 64rpx;
|
||
|
||
.brand-logo-wrap {
|
||
width: 140rpx;
|
||
height: 140rpx;
|
||
border-radius: 40rpx;
|
||
background: linear-gradient(135deg, #6366F1, #8B5CF6);
|
||
padding: 6rpx;
|
||
box-shadow: 0 12rpx 32rpx rgba(99, 102, 241, 0.4);
|
||
margin-bottom: 24rpx;
|
||
|
||
.brand-logo-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 36rpx;
|
||
}
|
||
}
|
||
|
||
.brand-app-name {
|
||
font-size: 38rpx;
|
||
font-weight: 800;
|
||
color: #F8FAFC;
|
||
letter-spacing: 2rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.brand-slogan {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
/* 主操作按钮区 */
|
||
.main-action-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-bottom: 40rpx;
|
||
|
||
.btn-wechat-primary {
|
||
width: 100%;
|
||
height: 92rpx;
|
||
border-radius: 46rpx;
|
||
background: #07C160;
|
||
color: #FFFFFF;
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.35);
|
||
border: none;
|
||
margin: 0;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
|
||
.wechat-icon-svg {
|
||
width: 38rpx;
|
||
height: 38rpx;
|
||
}
|
||
|
||
.btn-text {
|
||
letter-spacing: 1rpx;
|
||
}
|
||
}
|
||
|
||
.auth-guarantee-tip {
|
||
font-size: 20rpx;
|
||
color: #64748B;
|
||
margin-top: 14rpx;
|
||
}
|
||
}
|
||
|
||
/* 体验账号卡片 */
|
||
.demo-accounts-card {
|
||
background: #151D30;
|
||
border-radius: 28rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
padding: 28rpx 28rpx 16rpx;
|
||
margin-bottom: 40rpx;
|
||
|
||
.demo-card-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.demo-title {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
}
|
||
.demo-sub {
|
||
font-size: 20rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
|
||
.demo-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
|
||
.demo-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18rpx;
|
||
padding: 16rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
background: rgba(255, 255, 255, 0.03);
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
|
||
.demo-avatar {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
border-radius: 36rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.demo-info {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
|
||
.demo-name-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
margin-bottom: 4rpx;
|
||
|
||
.demo-name {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
}
|
||
|
||
.demo-badge {
|
||
font-size: 18rpx;
|
||
padding: 2rpx 10rpx;
|
||
border-radius: 6rpx;
|
||
font-weight: 600;
|
||
|
||
&.host {
|
||
background: rgba(245, 158, 11, 0.2);
|
||
color: #F59E0B;
|
||
}
|
||
&.buyer {
|
||
background: rgba(99, 102, 241, 0.2);
|
||
color: #818CF8;
|
||
}
|
||
&.expert {
|
||
background: rgba(16, 185, 129, 0.2);
|
||
color: #10B981;
|
||
}
|
||
}
|
||
}
|
||
|
||
.demo-feature {
|
||
font-size: 20rpx;
|
||
color: #94A3B8;
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.btn-switch-account {
|
||
font-size: 22rpx;
|
||
color: #6366F1;
|
||
font-weight: 600;
|
||
padding: 8rpx 18rpx;
|
||
border-radius: 12rpx;
|
||
background: rgba(99, 102, 241, 0.15);
|
||
border: none;
|
||
margin: 0;
|
||
line-height: 1.4;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 协议勾选 (一行紧凑完整展示) */
|
||
.protocol-section {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 10rpx;
|
||
margin-top: auto;
|
||
padding: 20rpx 0;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
flex-wrap: nowrap;
|
||
|
||
.checkbox {
|
||
width: 28rpx;
|
||
height: 28rpx;
|
||
border-radius: 6rpx;
|
||
border: 2rpx solid #64748B;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
|
||
&.checked {
|
||
background: #07C160;
|
||
border-color: #07C160;
|
||
|
||
.checkbox-inner {
|
||
width: 10rpx;
|
||
height: 10rpx;
|
||
background: #FFFFFF;
|
||
border-radius: 2rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.protocol-text {
|
||
font-size: 20rpx;
|
||
color: #94A3B8;
|
||
line-height: 1.2;
|
||
white-space: nowrap;
|
||
display: flex;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
|
||
.protocol-link {
|
||
color: #07C160;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 浅色暖阳模式适配 */
|
||
.theme-warm {
|
||
background-color: #FAF8F5;
|
||
|
||
.brand-header {
|
||
.brand-app-name {
|
||
color: #1C1917;
|
||
}
|
||
.brand-slogan {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
|
||
.main-action-section {
|
||
.auth-guarantee-tip {
|
||
color: #A8A29E;
|
||
}
|
||
}
|
||
|
||
.demo-accounts-card {
|
||
background: #FFFFFF;
|
||
border-color: #E7E5E4;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||
|
||
.demo-card-head {
|
||
.demo-title {
|
||
color: #1C1917;
|
||
}
|
||
.demo-sub {
|
||
color: #A8A29E;
|
||
}
|
||
}
|
||
|
||
.demo-list .demo-item {
|
||
background: #FDFBF7;
|
||
border-color: #E7E5E4;
|
||
|
||
.demo-info .demo-name-row .demo-name {
|
||
color: #1C1917;
|
||
}
|
||
|
||
.demo-info .demo-feature {
|
||
color: #78716C;
|
||
}
|
||
|
||
.btn-switch-account {
|
||
background: #FFF7ED;
|
||
color: #EA580C;
|
||
}
|
||
}
|
||
}
|
||
|
||
.protocol-section {
|
||
.checkbox {
|
||
border-color: #D6D3D1;
|
||
}
|
||
|
||
.protocol-text {
|
||
color: #78716C;
|
||
|
||
.protocol-link {
|
||
color: #07C160;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|