feat: add agreement-gated login state
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
<template>
|
||||
<view class="unauth-login-panel" :class="{ compact }">
|
||||
<view class="brand-section">
|
||||
<image class="brand-logo" src="/static/brand/logo.png" mode="aspectFit" />
|
||||
<text class="brand-name">大兴区冰上运动中心</text>
|
||||
</view>
|
||||
|
||||
<view class="login-card">
|
||||
<view class="unauth-copy" v-if="title || description">
|
||||
<text class="unauth-title" v-if="title">{{ title }}</text>
|
||||
<text class="unauth-description" v-if="description">{{ description }}</text>
|
||||
</view>
|
||||
|
||||
<button
|
||||
class="login-button"
|
||||
:class="{ disabled: loading }"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
@tap="handleLoginTap"
|
||||
>
|
||||
{{ primaryText }}
|
||||
</button>
|
||||
|
||||
<view class="agreement-checks">
|
||||
<view class="acceptance-row acceptance-user" @tap="toggleUserAgreement">
|
||||
<view class="checkbox" :class="{ checked: hasAcceptedUserAgreement }">
|
||||
<text v-if="hasAcceptedUserAgreement">✓</text>
|
||||
</view>
|
||||
<text class="acceptance-text">我已阅读并同意</text>
|
||||
<text class="agreement-inline-link" @tap.stop="openUserAgreement">用户协议</text>
|
||||
</view>
|
||||
<view class="acceptance-row acceptance-privacy" @tap="togglePrivacyPolicy">
|
||||
<view class="checkbox" :class="{ checked: hasAcceptedPrivacyPolicy }">
|
||||
<text v-if="hasAcceptedPrivacyPolicy">✓</text>
|
||||
</view>
|
||||
<text class="acceptance-text">我已阅读并同意</text>
|
||||
<text class="agreement-inline-link" @tap.stop="openPrivacyPolicy">隐私政策</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<AgreementLinks ref="agreementLinksRef" :show-entry="false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import AgreementLinks from '@/components/AgreementLinks.vue'
|
||||
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
primaryText: {
|
||||
type: String,
|
||||
default: '微信一键登录'
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['login'])
|
||||
|
||||
const hasAcceptedUserAgreement = ref(false)
|
||||
const hasAcceptedPrivacyPolicy = ref(false)
|
||||
const agreementLinksRef = ref(null)
|
||||
|
||||
const canLogin = computed(() => hasAcceptedUserAgreement.value && hasAcceptedPrivacyPolicy.value)
|
||||
|
||||
const toggleUserAgreement = () => {
|
||||
hasAcceptedUserAgreement.value = !hasAcceptedUserAgreement.value
|
||||
}
|
||||
|
||||
const togglePrivacyPolicy = () => {
|
||||
hasAcceptedPrivacyPolicy.value = !hasAcceptedPrivacyPolicy.value
|
||||
}
|
||||
|
||||
const openUserAgreement = () => {
|
||||
agreementLinksRef.value?.openUserAgreement()
|
||||
}
|
||||
|
||||
const openPrivacyPolicy = () => {
|
||||
agreementLinksRef.value?.openPrivacyPolicy()
|
||||
}
|
||||
|
||||
const handleLoginTap = () => {
|
||||
if (!canLogin.value) {
|
||||
uni.showToast({
|
||||
title: '请先勾选用户协议和隐私政策',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
emit('login')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.unauth-login-panel {
|
||||
min-height: calc(100vh - 176rpx - env(safe-area-inset-bottom));
|
||||
padding: 0 $spacing-lg 48rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
gap: 58rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
|
||||
&.compact {
|
||||
min-height: 640rpx;
|
||||
padding: 0 $spacing-lg 40rpx;
|
||||
gap: 44rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-section {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
width: 144rpx;
|
||||
height: 144rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
margin-top: 34rpx;
|
||||
font-size: 40rpx;
|
||||
line-height: 1.26;
|
||||
font-weight: 900;
|
||||
color: $text-color-main;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 100%;
|
||||
max-width: 560rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.unauth-copy {
|
||||
margin-bottom: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: $spacing-sm;
|
||||
max-width: 560rpx;
|
||||
}
|
||||
|
||||
.unauth-title {
|
||||
font-size: 30rpx;
|
||||
line-height: 1.35;
|
||||
font-weight: 800;
|
||||
color: $text-color-main;
|
||||
}
|
||||
|
||||
.unauth-description {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
color: $text-color-muted;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
border-radius: $border-radius-base;
|
||||
color: #FFFFFF;
|
||||
background-color: $text-color-main;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.disabled,
|
||||
&[disabled] {
|
||||
color: #FFFFFF;
|
||||
background-color: $text-color-light;
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-checks {
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.acceptance-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 38rpx;
|
||||
text-align: left;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
border: 2rpx solid $border-color;
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #FFFFFF;
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
font-weight: 900;
|
||||
background-color: #FFFFFF;
|
||||
|
||||
&.checked {
|
||||
border-color: $text-color-main;
|
||||
background-color: $text-color-main;
|
||||
}
|
||||
}
|
||||
|
||||
.acceptance-text,
|
||||
.agreement-inline-link {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.acceptance-text {
|
||||
color: $text-color-muted;
|
||||
}
|
||||
|
||||
.agreement-inline-link {
|
||||
color: $text-color-main;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user