feat(compliance): 完善微信内容安全检测、用户隐私保护指引弹窗与主包分包加载架构
这个提交包含在:
374
pwuniapp/components/PrivacyModal.vue
普通文件
374
pwuniapp/components/PrivacyModal.vue
普通文件
@@ -0,0 +1,374 @@
|
||||
<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" scoped>
|
||||
.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>
|
||||
在新工单中引用
屏蔽一个用户