文件
shanghaipeiwan/pwuniapp/pages/auth/login.vue

600 行
16 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<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>