1、相关api说明
适配过程中主要是用到了以下几个接口
1. wx.onNeedPrivacyAuthorization:用于监听隐私接口需要用户授权事件,只有当隐私协议需要用户授权时才会由平台触发此事件,然后开发者需要弹窗显示隐私协议说明。如果用户拒绝授权,则隐私接口调用失败,在下次调用到隐私接口时还会继续弹。
2. wx.requirePrivacyAuthorize:用于模拟隐私接口调用,并触发隐私弹窗逻辑,也就是会触发wx.onNeedPrivacyAuthorization,但如果用户之前已经同意过隐私授权,会立即返回success回调,不会触发 wx.onNeedPrivacyAuthorization 。这个api的使用场景目前我是用在获取用户昵称时,在下一篇文章会进行说明。
3. wx.openPrivacyContract:用于打开隐私协议页面。
其他api大家可以根据具体情况选择使用。
2、弹窗思路
其实就是写一个自定义组件,然后在有调用到隐私接口的页面引入,利用自定义组件的attached方法,把各个页面的隐私弹窗组件的显示、隐藏方法保存到自定义组件的全局变量中,当用户点击隐私协议弹窗的同意、拒绝按钮时调用resolve方法,将对应的参数通知给平台。
3、注意点
- 隐私接口在隐私保护指引中有声明才能调用
- 基础库版本2.32.3及以上开始支持
- 2023.9.15号之前,在 app.json 中配置 __usePrivacyCheck__: true 后,会启用隐私相关功能,如果不配置或者配置为 false 则不会启用。2023.9.15号之后,不论 app.json 中是否有配置 __usePrivacyCheck__,隐私相关功能都会启用
- wx.onNeedPrivacyAuthorization 是覆盖式注册监听,若重复注册监听,则只有最后一次注册会生效
- 同意授权后如果想取消授权,在开发工具上 清缓存->清除模拟器缓存->清除授权数据,在手机上删除小程序即可
- 隐私弹窗的z-index要设置成最大,避免被其他遮罩挡住导致无法点击,另外如果有调到官方的api例如wx.showLoading,也要注意是否设置了mask,避免在loading的时候弹出隐私弹窗,导致弹窗无法点击,而loading又要等弹窗关闭才会消失的尴尬情况
4、代码如下
4.1、app.json
"usingComponents": {
//全局自定义组件
"privacyPopup": "/components/privacy/privacyPopup"
},
//开启隐私相关功能
"__usePrivacyCheck__": true
4.2、自定义组件privacyPopup
4.2.1、privacyPopup.wxml
<view class="container" wx:if="{{show}}">
<view class="cover {{showCoverAnimation?'cover-fade-in':''}}" catch:touchmove="return"></view>
<view class="privacy-box {{showBoxAnimation?'slade-in':''}} {{device.isPhoneX? 'phx_68':''}}" catch:touchmove="return">
<view class="title flex-start-horizontal">
<view class="logo" wx:if="{{privacyConfig.icon}}">
<image class="icon" src="{{privacyConfig.icon}}"></image>
</view>
<view class="mini-name">{{privacyConfig.name || '小程序'}}</view>
</view>
<view class="tips">
<view class="privacy-content">
<view class="start">{{privacyConfig.content.start}}</view>
<view class="link" bindtap="openPrivacyContract">
{{privacyConfig.content.mid}}
</view>
<view class="end">{{privacyConfig.content.end}}</view>
</view>
</view>
<view class="buttons flex-center">
<button class="cancel reset-btn" bindtap="disagree">拒绝</button>
<button class="save reset-btn" id="agree-btn" open-type="agreePrivacyAuthorization" bindagreeprivacyauthorization="agree">同意</button>
</view>
</view>
</view>
4.2.2、privacyPopup.wxss
@import "/app.wxss";
.cover{
background-color: #111;
opacity: 0;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 50000;
transition: opacity .3s;
}
.privacy-box{
position: fixed;
left: 0rpx;
right: 0rpx;
bottom: 0rpx;
z-index: 51000;
background-color: #fff;
box-sizing: border-box;
padding-top: 60rpx;
padding-left: 50rpx;
padding-right: 50rpx;
border-radius: 30rpx 30rpx 0 0;
transform: translateY(100%);
transition: transform .3s, bottom .3s;
}
.cover-fade-in{
opacity: 0.7;
}
.slade-in{
transform: translateY(0);
bottom: 0rpx;
}
.privacy-box .title{
font-size: 32rpx;
font-weight: 500;
text-align: center;
margin-bottom: 40rpx;
color: #161616;
}
.privacy-box .title .icon{
width: 46rpx;
height: 46rpx;
margin-right: 8rpx;
vertical-align: bottom;
border-radius: 50%;
}
.privacy-box .title .mini-name{
/*margin-bottom: 2rpx;*/
}
.privacy-box .tips{
margin-bottom: 20rpx;
}
.privacy-box .tips .privacy-content{
color: #606266;
font-size: 30rpx;
margin-bottom: 20rpx;
line-height: 1.6;
}
.privacy-box .tips .privacy-content .start,
.privacy-box .tips .privacy-content .link,
.privacy-box .tips .privacy-content .end {
display: inline;
}
.privacy-box .tips .privacy-content .link{
color: #1890FF;
}
.privacy-box .buttons{
margin-bottom: 40rpx;
margin-top: 50rpx;
text-align: center;
font-size: 34rpx;
font-weight: 550;
}
.privacy-box .buttons .save{
width: 220rpx !important;
height: 90rpx;
line-height: 90rpx;
border-radius: 14rpx;
color: #fff;
/*background:#fff linear-gradient(90deg,rgba(246, 120, 121,.9) 10%, rgb(246, 120, 121));*/
background-color: #07c160;
margin-left: -50rpx;
}
.privacy-box .buttons .cancel{
width: 220rpx !important;
height: 90rpx;
line-height: 90rpx;
border-radius: 14rpx;
color: #07c160;
background-color: #F2F2F2;
}
4.2.3、privacyPopup.js
//获取应用实例
const tabbar = require("../../utils/tabbar.js");
const app = getApp();
//用来保存各个页面注册的隐私协议回调事件
let privacyHooks = {};
if (wx.onNeedPrivacyAuthorization) {
console.warn("当前基础库支持api wx.onNeedPrivacyAuthorization");
wx.onNeedPrivacyAuthorization(resolve => {
console.warn("需要隐私协议弹窗:", resolve);
const pages = getCurrentPages();
const route = pages[pages.length - 1].route;
const hook = privacyHooks[route];
if (hook) {
hook.resolve = resolve;
hook.show(resolve);
} else {
console.error("当前页面没有注册隐藏协议弹窗回调:", route);
}
})
} else {
console.warn("当前基础库不支持api wx.onNeedPrivacyAuthorization");
}
Component({
data: {
show: false,
showCoverAnimation: false,//显示类别选择窗口动画
showBoxAnimation: false,//显示类别选择窗口动画
},
lifetimes: {
//在组件实例进入页面节点树时执行
attached: function () {
console.warn("privacyPopup attached");
const pages = getCurrentPages();
privacyHooks[pages[pages.length - 1].route] = {
show: resolve => {
this.show(resolve);
},
close: () => {
this.hide();
}
}
},
//在组件实例被从页面节点树移除时执行
detached: function () {
console.warn("privacyPopup detached");
}
},
methods: {
/**
* 同意隐私协议
* @param e
*/
agree(e) {
Object.values(privacyHooks).forEach(hook => {
hook.close();
hook.resolve && hook.resolve({
event: "agree",
buttonId: "agree-btn"
});
});
},
/**
* 不同意隐私协议
* @param e
*/
disagree(e) {
Object.values(privacyHooks).forEach(hook => {
hook.close();
hook.resolve && hook.resolve({
event: "disagree"
});
});
},
/**
* 显示隐私协议授权弹窗
*/
show(resolve) {
app.fillConfig(this, ["privacyConfig"], data => {
console.log("显示隐私协议弹窗");
const device = app.getSystemInfo();
this.setData({
show: true,
device,
}, () => {
this.setData({
showCoverAnimation: true,
showBoxAnimation: true
});
//自定义隐私弹窗曝光告知平台
resolve({event: "exposureAuthorization"});
});
tabbar.hideTabByPrivacy(this);
});
},
/**
* 关闭隐私协议授权弹窗
*/
hide() {
console.log("隐藏隐私协议弹窗");
this.setData({
showCoverAnimation: false,
showBoxAnimation: false
}, () => {
const that = this;
setTimeout(function () {
that.setData({
show: false
})
}, 300)
})
tabbar.showTabByPrivacy(this);
},
/**
* 打开隐私协议
*/
openPrivacyContract() {
wx.openPrivacyContract({
success: res => {
console.log("openPrivacyContract success")
},
fail: res => {
console.error("openPrivacyContract fail", res)
}
})
}
}
})
5、弹窗效果
有没有低版本的兼容方案
点赞!
tabbar.js?
老兄,您这个弹窗仍然是页面初始化的时候弹出来,现在我是想要点击按钮,因为这按钮会使用摄像头,所以点击按钮的时候弹出来
请问一下,比较老的小程序,项目里project.config.json文件中定义的基础库版本是2.11.3是不是就不用改这个隐私设置了,会影响之后的隐私接口调用吗?
"__usePrivacyCheck__": true 这个是不是 可以不用写了
不错!