评论

适配隐私保护协议接口

简单分享一下如何适配隐私协议

1、相关api说明

适配过程中主要是用到了以下几个接口

1. wx.onNeedPrivacyAuthorization:用于监听隐私接口需要用户授权事件,只有当隐私协议需要用户授权时才会由平台触发此事件,然后开发者需要弹窗显示隐私协议说明。如果用户拒绝授权,则隐私接口调用失败,在下次调用到隐私接口时还会继续弹。

2. wx.requirePrivacyAuthorize:用于模拟隐私接口调用,并触发隐私弹窗逻辑,也就是会触发wx.onNeedPrivacyAuthorization,但如果用户之前已经同意过隐私授权,会立即返回success回调,不会触发 wx.onNeedPrivacyAuthorization 。这个api的使用场景目前我是用在获取用户昵称时,在下一篇文章会进行说明。

3. wx.openPrivacyContract:用于打开隐私协议页面。

其他api大家可以根据具体情况选择使用。

2、弹窗思路

其实就是写一个自定义组件,然后在有调用到隐私接口的页面引入,利用自定义组件的attached方法,把各个页面的隐私弹窗组件的显示、隐藏方法保存到自定义组件的全局变量中,当用户点击隐私协议弹窗的同意、拒绝按钮时调用resolve方法,将对应的参数通知给平台。

3、注意点

  1. 隐私接口在隐私保护指引中有声明才能调用
  2. 基础库版本2.32.3及以上开始支持
  3. 2023.9.15号之前,在 app.json 中配置 __usePrivacyCheck__: true 后,会启用隐私相关功能,如果不配置或者配置为 false 则不会启用。2023.9.15号之后,不论 app.json 中是否有配置 __usePrivacyCheck__,隐私相关功能都会启用
  4. wx.onNeedPrivacyAuthorization 是覆盖式注册监听,若重复注册监听,则只有最后一次注册会生效
  5. 同意授权后如果想取消授权,在开发工具上 清缓存->清除模拟器缓存->清除授权数据,在手机上删除小程序即可
  6. 隐私弹窗的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、弹窗效果




最后一次编辑于  2023-09-13  
点赞 4
收藏
评论

7 个评论

  • 凉白开
    凉白开
    04-19

    有没有低版本的兼容方案

    04-19
    赞同
    回复
  • 泰酷拉
    泰酷拉
    03-27

    点赞!

    03-27
    赞同
    回复
  • HYF
    HYF
    2023-09-12

    tabbar.js?

    2023-09-12
    赞同
    回复 2
    • showms
      showms
      2023-09-12
      这个是我自己封装的用来操作tabbar的,可以忽略
      2023-09-12
      回复
    • 醉辰风
      醉辰风
      2023-09-13回复showms
      我现在隐私协议填写之后审核,审核通过之后调用官方提供的查看协议的接口去查看,为什么会展示暂无收集信息呢,日期也是1970.01.01,
      2023-09-13
      回复
  • 包建强
    包建强
    2023-09-09

    老兄,您这个弹窗仍然是页面初始化的时候弹出来,现在我是想要点击按钮,因为这按钮会使用摄像头,所以点击按钮的时候弹出来

    2023-09-09
    赞同
    回复 1
    • showms
      showms
      2023-09-10
      不是初始化的时候弹,初始化只是注册回调函数,真正在调隐私api的时候会去回调之前注册好的函数
      2023-09-10
      回复
  • 四叶草
    四叶草
    2023-09-06

    请问一下,比较老的小程序,项目里project.config.json文件中定义的基础库版本是2.11.3是不是就不用改这个隐私设置了,会影响之后的隐私接口调用吗?

    2023-09-06
    赞同
    回复 2
    • showms
      showms
      2023-09-06
      都要改,没记错的话project.config.json的配置只是针对调试工具的基础库版本
      2023-09-06
      回复
    • 四叶草
      四叶草
      2023-09-06回复showms
      好的,感谢解答!
      2023-09-06
      回复
  • 亮
    2023-09-06
    "__usePrivacyCheck__": true 
    
    这个是不是 可以不用写了
    
    2023-09-06
    赞同
    回复 3
    • showms
      showms
      2023-09-06
      写了也没事
      2023-09-06
      回复
    • 包建强
      包建强
      2023-09-09
      必须写,不然弹不出来
      2023-09-09
      回复
    • showms
      showms
      2023-09-10回复包建强
      那是9.15号前不会弹出来,9.15号后都会弹
      2023-09-10
      回复
  • 陆大师
    陆大师
    发表于小程序端
    2023-08-31

    不错!

    2023-08-31
    赞同
    回复
登录 后发表内容