文件
shanghaipeiwan/pwuniapp/components/PrivacyModal.vue

375 行
9.6 KiB
Vue

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

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

<template>
<view class="privacy-modal-mask" v-if="visible" @click.stop>
<view class="privacy-dialog" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
<!-- 弹窗顶部标题 -->
<view class="dialog-header">
<view class="badge-icon-box">
<image class="shield-icon" :src="ICONS.shield" mode="aspectFit" />
</view>
<text class="dialog-title">用户隐私保护指引</text>
<text class="dialog-sub">同城陪伴绿色健康服务规范</text>
</view>
<!-- 协议条款滚动正文 -->
<scroll-view class="dialog-scroll" scroll-y>
<view class="privacy-content">
<text class="paragraph">
感谢您使用同城生活陪伴服务平台!在您使用本平台提供的线上找搭子、预约陪玩、附近雷达扫单、动态发布等服务前,请您认真阅读并充分理解
<text class="link-text" @click="openPrivacyDoc">《用户服务协议》</text>
与
<text class="link-text" @click="openPrivacyDoc">《个人信息保护与隐私指引》</text>。
</text>
<view class="clause-item">
<text class="clause-title">1. 设备与多媒体权限说明</text>
<text class="clause-desc">
为了向您提供更改头像、发表动态、实名认证等服务,我们需要申请使用您的相册读取与相机权限;为了向您提供语音名片与打招呼试听服务,我们需要申请麦克风录音权限。
</text>
</view>
<view class="clause-item">
<text class="clause-title">2. 地理位置与现场核销说明</text>
<text class="clause-desc">
为了帮您智能匹配 5km 附近的组局活动和大神,我们需要获取您的精确定位信息;现场履约时将通过 4 位动态核销码进行到场安全校验,全流程杜绝非公开场所履约。
</text>
</view>
<view class="clause-item">
<text class="clause-title">3. 平台合规与绿色社交守则</text>
<text class="clause-desc">
平台严格执行微信内容安全风控标准(包含文本与多媒体检测)。严禁发布任何涉黄低俗、虚假诈骗、引流私下交易等违规内容,一经核实将永久封禁账号并追究法律责任。
</text>
</view>
</view>
</scroll-view>
<!-- 底部操作按钮 -->
<view class="dialog-footer">
<button class="btn-action btn-disagree btn-active" @click="handleDisagree">
暂不同意
</button>
<!-- #ifdef MP-WEIXIN -->
<button
id="agree-btn"
class="btn-action btn-agree btn-active"
open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handleAgree"
>
同意并继续
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<button class="btn-action btn-agree btn-active" @click="handleAgree">
同意并继续
</button>
<!-- #endif -->
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ICONS } from '@/common/icons.js';
import { getCurrentThemeKey } from '@/common/theme.js';
const visible = ref(false);
const currentThemeKey = ref(getCurrentThemeKey());
let privacyResolve = null;
const STORAGE_KEY_PRIVACY_AGREED = 'PW_USER_PRIVACY_AGREED_V1';
const checkPrivacyAgreed = () => {
try {
return Boolean(uni.getStorageSync(STORAGE_KEY_PRIVACY_AGREED));
} catch (e) {
return false;
}
};
const setPrivacyAgreed = (val) => {
try {
uni.setStorageSync(STORAGE_KEY_PRIVACY_AGREED, val ? 1 : 0);
} catch (e) {}
};
onMounted(() => {
currentThemeKey.value = getCurrentThemeKey();
uni.$on('theme-change', (theme) => {
currentThemeKey.value = theme;
});
// #ifdef MP-WEIXIN
if (typeof wx !== 'undefined' && wx.onNeedPrivacyAuthorization) {
wx.onNeedPrivacyAuthorization((resolve) => {
privacyResolve = resolve;
visible.value = true;
});
}
// #endif
// 若本地尚未同意过且处于未授权态,首次进入提示
if (!checkPrivacyAgreed()) {
visible.value = true;
}
});
const openPrivacyDoc = () => {
// #ifdef MP-WEIXIN
if (typeof wx !== 'undefined' && wx.openPrivacyContract) {
wx.openPrivacyContract({
fail: () => {
uni.showToast({ title: '无法打开隐私协议详情', icon: 'none' });
}
});
return;
}
// #endif
uni.showModal({
title: '用户服务与隐私协议',
content: '本平台严格遵循国家法律法规与微信公众平台服务条款,确保您的数据与隐私安全。如需查看完整条款可访问平台官方网站。',
showCancel: false,
confirmText: '我知道了'
});
};
const handleAgree = () => {
setPrivacyAgreed(true);
visible.value = false;
if (privacyResolve) {
privacyResolve({ buttonId: 'agree-btn', event: 'agree' });
privacyResolve = null;
}
uni.$emit('privacy-agreed');
};
const handleDisagree = () => {
visible.value = false;
if (privacyResolve) {
privacyResolve({ event: 'disagree' });
privacyResolve = null;
}
uni.showToast({
title: '您已拒绝隐私指引,部分定位与媒体功能将受限',
icon: 'none',
duration: 2000
});
};
defineExpose({
show: () => { visible.value = true; },
hide: () => { visible.value = false; }
});
</script>
<style lang="scss">
.privacy-modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(8px);
z-index: 99999;
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
}
.privacy-dialog {
width: 100%;
max-width: 620rpx;
background: #151D30;
border-radius: 32rpx;
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 24rpx 60rpx rgba(0, 0, 0, 0.6);
padding: 40rpx 36rpx;
box-sizing: border-box;
animation: dialogPop 0.25s cubic-bezier(0.16, 1, 0.3, 1);
&.theme-warm {
background: #FFFFFF;
border-color: #E2E8F0;
box-shadow: 0 24rpx 60rpx rgba(0, 0, 0, 0.12);
.dialog-header {
.badge-icon-box {
background: rgba(234, 88, 12, 0.1);
border-color: rgba(234, 88, 12, 0.25);
}
.dialog-title {
color: #1E293B;
}
.dialog-sub {
color: #64748B;
}
}
.dialog-scroll {
.privacy-content {
.paragraph {
color: #475569;
.link-text {
color: #EA580C;
}
}
.clause-item {
background: #F8FAFC;
border-color: #E2E8F0;
.clause-title {
color: #1E293B;
}
.clause-desc {
color: #64748B;
}
}
}
}
.dialog-footer {
.btn-disagree {
background: #F1F5F9;
color: #64748B;
border-color: #CBD5E1;
}
.btn-agree {
background: linear-gradient(135deg, #EA580C 0%, #F97316 100%);
color: #FFFFFF;
box-shadow: 0 8rpx 20rpx rgba(234, 88, 12, 0.3);
}
}
}
.dialog-header {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin-bottom: 24rpx;
.badge-icon-box {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background: rgba(99, 102, 241, 0.15);
border: 1px solid rgba(99, 102, 241, 0.3);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
.shield-icon {
width: 44rpx;
height: 44rpx;
}
}
.dialog-title {
font-size: 34rpx;
font-weight: 700;
color: #F8FAFC;
letter-spacing: 0.5rpx;
}
.dialog-sub {
font-size: 24rpx;
color: #94A3B8;
margin-top: 6rpx;
}
}
.dialog-scroll {
max-height: 440rpx;
margin-bottom: 32rpx;
.privacy-content {
.paragraph {
font-size: 26rpx;
line-height: 1.6;
color: #CBD5E1;
display: block;
margin-bottom: 20rpx;
.link-text {
color: #818CF8;
font-weight: 600;
cursor: pointer;
}
}
.clause-item {
background: #0A0F1D;
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16rpx;
padding: 16rpx 20rpx;
margin-bottom: 16rpx;
.clause-title {
font-size: 25rpx;
font-weight: 600;
color: #F1F5F9;
display: block;
margin-bottom: 8rpx;
}
.clause-desc {
font-size: 23rpx;
line-height: 1.5;
color: #94A3B8;
display: block;
}
}
}
}
.dialog-footer {
display: flex;
gap: 20rpx;
.btn-action {
flex: 1;
height: 76rpx;
border-radius: 38rpx;
font-size: 28rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
border: none;
outline: none;
margin: 0;
&::after {
border: none;
}
&.btn-disagree {
background: rgba(255, 255, 255, 0.08);
color: #94A3B8;
border: 1px solid rgba(255, 255, 255, 0.12);
}
&.btn-agree {
background: linear-gradient(135deg, #6366F1 0%, #4F46E5 100%);
color: #FFFFFF;
box-shadow: 0 8rpx 24rpx rgba(99, 102, 241, 0.4);
}
}
}
}
@keyframes dialogPop {
0% {
opacity: 0;
transform: scale(0.92);
}
100% {
opacity: 1;
transform: scale(1);
}
}
</style>