微信小程序隐私协议引入开发教程和uniapp 最佳实现方式


前言

微信小程序从2023年9月15日开始,涉及处理用户个人信息的小程序开发者,需通过弹窗等明显方式提示用户阅读隐私政策等收集使用规则。为规范开发者的用户个人信息处理行为,保障用户合法权益,微信要求开发者主动同步微信当前用户已阅读并同意小程序的隐私政策等收集使用规则,方可调用微信提供的隐私接口。简而言之,就是调用某些涉及用户隐私api比如获取用户位置(wx.getLocation)、选择地址(wx.chooseAddress)、选择照片(wx.chooseImage)等api(隐私接口)前需要弹出隐私政策告知用户取得同意后方可正常使用功能。

需要注意的是所有涉及到隐私接口整个小程序只要同意一次隐私保护后所有api都能正常使用,本地也有相应生成缓存记录,如果删除小程序就需要重新同意。

在这里插入图片描述

一、相关文章

可查阅微信官方说明:
用户隐私保护指引填写说明
小程序隐私协议开发指南
需要隐私协议的隐私接口(api)

二、功能设计

  对于弹窗时机设计可以分为两种,第一中比较暴力做法就是在首页进入立即弹出隐私同意窗口,
 不同意直接退出小程序。第二种比较友好方式在每次需要调用隐私api前检查根据需要弹窗。

ps: 本文将主要采用第二种方式进行开发,并以uniapp开发举例说明

三、开发前准备

步骤一

需要在小程序后台-设置-服务内容声明-用户隐私保护指引进行更新填写,具体可查看上面相关文章——用户隐私保护指引填写说明,提交并审核通过后才能正常使用
在这里插入图片描述

步骤二

在 2023年10月17日之前,在 app.json 中配置 usePrivacyCheck: true 后,会启用隐私相关功能,如果不配置或者配置为 false 则不会启用。在 2023年10月17日之后,不论 app.json 中是否有配置 usePrivacyCheck,隐私相关功能都会启用。

如果是uniapp开发在manifest.json切换到源码试图在mp-weixin字段内添加"usePrivacyCheck": true

 "mp-weixin" : {
		"__usePrivacyCheck__": true
    },

步骤三

相关api介绍

1.wx.getPrivacySetting(Object object)
查询隐私授权情况,成功回调参数needAuthorization字段表示是否需要用户授权隐私协议

wx.getPrivacySetting({
      success: res => {
        console.log(res) // 返回结果为: res = { needAuthorization: true/false, privacyContractName: '《xxx隐私保护指引》' }
        if (res.needAuthorization) {
          // 需要弹出隐私协议
        } else {
          // 用户已经同意过隐私协议,所以不需要再弹出隐私协议,也能调用隐私接口
        }
      },
      fail: () => {},
      complete: () => {}
    })

2.wx.openPrivacyContract(Object object)
跳转至隐私协议页面

wx.openPrivacyContract({
  success: () => {}, // 打开成功
  fail: () => {}, // 打开失败
  complete: () => {}
})

3.<button open-type=“agreePrivacyAuthorization>”
用户同意隐私协议按钮。用户点击一次此按钮后,所有已声明过的隐私接口可以正常调用。可通过 bindagreeprivacyauthorization 监听用户同意隐私协议事件

四、开发前分析

对于隐私弹窗需要封装一个全局组件,在需要的页面引入。调用隐私api前根据wx.getPrivacySetting的needAuthorization判断是否需要弹窗,如果值为flase不需要,true就需要弹窗等待用户同意,在同意按钮bindagreeprivacyauthorization回调后实现隐私api逻辑。

假如很多页面需要用到隐私api,每个页面要单独引入组件标签,有没有什么方法可以全局引入不用单独写呢,答案是有——vue-template-compiler,关于vue-template-compiler有很多文章可以自行查阅这里不在细述。

组件引入问题解决了,但是每个页面逻辑还要重复写,是否有更好更便捷调用方式呢?于是我们想到了类似加载组件的使用,uni.showToast()通过全局api就能控制是否显示。

于是我们最终封装目标如下:

this.$privacyCheck({
	 agree:()=>{},//已同意回调
	 disAgree:()=>{},//不同意回调
	 complete:()=>{}//完成回调
 })

在每个页面只需通过this.$privacyCheck()很方便调用

五、代码实现

1.隐私弹窗组件

components/privacyDialog/index.vue

<!-- 隐私授权对话框 -->
<template>
	<view class="comp-container" v-if="visible">
		<view class="dialog">
			<view class="title">用户隐私保护提示</view>
			<view class="desc">
				感谢您使用本小程序,在使用前您应当阅读并同意<text class="privacy"
					@click="handleOpenPrivacyContract">《用户隐私保护指引》</text>,当点击同意,即表示您已理解并同意该条款内容,该条款将对您产生法律约束力;如您不同意,将无法继续使用小程序相关功能。
			</view>
			<view class="footer">
				<button type="default" class="btn disagree" @click="handleDisagree">不同意</button>
				<button type="default" open-type="agreePrivacyAuthorization" class="btn agree"
					@agreeprivacyauthorization="handleAgree">同意</button>

			</view>
		</view>
	</view>
</template>
<script>
	export default {
		data() {
			return {
				visible: false,
				agreeCallBack: null, //同意回调函数
				disAgreeCallBack: null, //不同意回调函数
				completeCallBack: null //完成回调函数
			}
		},
		methods: {
			//检查根据是否需要并弹出隐私窗口
			check(options = {}) {
				let {
					agree,
					disAgree,
					complete
				} = options
				if (uni.getPrivacySetting) {
					if (typeof complete === 'function') {
						this.completeCallBack = complete
					}
					uni.getPrivacySetting({
						success: res => {
							if (typeof agree === 'function') {
								this.agreeCallBack = agree
							}
							if (typeof disAgree === 'function') {
								this.disAgreeCallBack = disAgree
							}
							//需要隐私同意显示对话框
							if (res.needAuthorization) {
								this.visible = true
							} else {
								this.handleAgree()
							}
						},
						fail: () => {
							this.handleComplete()
						}
					})
				}

			},
			//隐藏对话框
			hide() {
				this.visible = false
			},
			//不同意
			handleDisagree() {
				this.visible = false
				if (typeof this.disAgreeCallBack === 'function') {
					this.disAgreeCallBack()
				}
				this.handleComplete()
			},
			//同意
			handleAgree() {
				this.visible = false;
				if (typeof this.agreeCallBack === 'function') {
					this.agreeCallBack()
				}
				this.handleComplete()

			},
			//完成
			handleComplete() {
				if (typeof this.completeCallBack === 'function') {
					this.completeCallBack()
				}
			},
			//打开隐私政策页面
			handleOpenPrivacyContract() {
				uni.openPrivacyContract()
			}
		}


	};
</script>
<style lang="scss" scoped>
	.comp-container {
		position: fixed;
		top: 0;
		right: 0;
		left: 0;
		bottom: 0;
		background-color: rgba(0, 0, 0, 0.6);
		z-index: 999;
	}

	.dialog {
		color: #333;
		position: absolute;
		top: 50%;
		left: 50%;
		transform: translate(-50%, -50%);
		background-color: #fff;
		border-radius: 20rpx;
		padding: 50rpx 40rpx;
		width: 560rpx;
		box-sizing: border-box;

		.title {
			width: 100%;
			color: #000;
			text-align: center;
			font-size: 32rpx;
			font-weight: 650;
			box-sizing: border-box;
		}

		.desc {
			line-height: 40rpx;
			box-sizing: border-box;
			margin-top: 50rpx;
			font-size: 26rpx;

			.privacy {
				color: #2f80ed;
			}
		}

		.footer {
			width: 100%;
			display: flex;
			align-items: center;
			justify-content: space-between;
			margin-top: 30rpx;

			.btn {
				display: flex;
				align-items: center;
				color: #FFF;
				font-size: 28rpx;
				font-weight: 500;
				line-height: 80rpx;
				text-align: center;
				height: 80rpx;
				border-radius: 10rpx;
				border: none;
				background: #07c160;
				flex: 1;
				justify-content: center;

				&:nth-last-child(1) {
					margin-left: 30rpx;
				}

				&.disagree {
					color: #333;
					background: #f2f2f2;
				}
			}
		}

	}
</style>

2.privacyCheck.js

components/privacyDialog/privacyCheck.js
检测是否需要隐私弹窗

/**隐私授权弹窗全局调用,agree同意或者已同意过(不需要弹窗)回调,disagree不同意回调,complete:所有情况都走该回调
 * this.$privacyCheck({
	 agree:()=>{},
	 disAgree:()=>{},
	 complete:()=>{}
 })
 */
const privacyCheck = (options = {}) => {
	const pages = getCurrentPages();
	let curPage = pages[pages.length - 1];//当前页面
	curPage.$vm.$refs.privacyDialog.check(options)

}

export default privacyCheck

3.main.js

import privacyCheck from '@/components/privacyDialog/privacyCheck.js'
import privacyDialog from '@/components/privacyDialog/index.vue'
Vue.component('privacyDialog',privacyDialog)
Vue.prototype.$privacyCheck=privacyCheck

4.vue.config.js

此处为vue-template-compiler处理,在每个页面templae加入隐私弹窗组件标签 。如果没有这个文件,新建一个写入如下代码:

module.exports = {
  chainWebpack: config => {
    config.module.rule('vue').use('vue-loader').loader('vue-loader').tap(options => {
      const compile = options.compiler.compile
      options.compiler.compile = (template, ...args) => {
        if (args[0].resourcePath.match(/^pages/)) {
          template = template.replace(/[\s\S]+?<[\d\D]+?>/, _ => `${_}
	        <privacyDialog ref="privacyDialog" />
	  `)
        }
        return compile(template, ...args)
      }
      return options
    })
  }
}

5.页面使用

manifest.json

 /* 小程序特有相关 */
    "mp-weixin" : {
		"requiredPrivateInfos" : [ "chooseAddress", "getLocation" ],
		"permission" : {
		    "scope.userLocation" : {
		        "desc" : "你的位置信息将用于小程序位置接口的效果展示"
		    }
		},
		"__usePrivacyCheck__": true
    },

index.vue

<template>
	<view class="content">
		<button @click="handleChoseAddress">选择地址</button>
	</view>
	
</template>

<script>
	export default {
		data() {
			return {

			}
		},
		mounted(){
			//定位
            this.handleLocation()
		},
		methods: {
			//定位
			handleLocation() {
				this.$privacyCheck({
					//已同意
					agree:()=>{
						uni.getLocation({
							success(e) {
								console.log(e)
							}
						})
					},
					//不同意
					disAgree:()=>{
						console.log('不同意')
					}
				})
			},
			//选择地址
			handleChoseAddress(){
				this.$privacyCheck({
					//已同意
					agree:()=>{
						uni.chooseAddress({
							success(e) {
								console.log(e)
							}
						})
					},
					//不同意
					disAgree:()=>{
						console.log('不同意')
					}
				})
			}
		}
	}
</script>

注意:this.$privacyCheck必须在mounted周期函数后(组件渲染完成)使用
在这里插入图片描述

五、总结

通过上述封装就能很方便快速在页面调用隐私弹窗,目前隐私弹窗组件需要自行实现,根据官方说明后续更新可能会提供官方组件,到时又多了一种选择。

六、插件示例

传送门:点击查看

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pixle0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值