离9月16只剩最后一周了,还有多少小伙伴和我一样还没上线微信要求的 小程序隐私协议。趁今天不忙赶紧把这个开发了。
新提供的四个API
wx.onNeedPrivacyAuthorization
监听隐私接口需要用户授权事件。wx.openPrivacyContract
跳转至隐私协议页面。wx.getPrivacySetting
查询隐私授权情况。wx.requirePrivacyAuthorize
模拟隐私接口调用,并触发隐私弹窗逻辑。
其中后面两个不是必须要用,看业务逻辑。一般我们用wx.onNeedPrivacyAuthorization
和 wx.openPrivacyContract
就可以。微信社区有更多文章介绍。在这里就说明常见流程:
用户触发隐私接口 --> wx.onNeedPrivacyAuthorization
的回调函数能监听到了,开发者弹出隐私窗口 --> 弹窗提供隐私协议链接,通过 wx.openPrivacyContract
调用跳转 --> 用户可选择性看完后点 同意按钮:<button id="agree-btn" open-type="agreePrivacyAuthorization" bindagreeprivacyauthorization="handleAgree">同意</button>
--> 触发按钮事件即成功。
Taro项目中使用
Taro项目中使用 openType 为 agreePrivacyAuthorization 的 Button 组件在新版本可以直接用 ,如果旧版需要做一些处理,github上有 issue。
先安装 @tarojs/plugin-inject
bash
npm i @tarojs/plugin-inject -D
然后 config/index.js
中增加 plugins
配置:
javascript
{
// ...
plugins:[
[
"@tarojs/plugin-inject",
{
components: {
Button: {
bindagreeprivacyauthorization: ""
}
}
}
]
]
}
最后 Button 组件就可正常使用。
jsx
<Button
// @ts-ignore
openType="agreePrivacyAuthorization"
// @ts-ignore
onAgreePrivacyAuthorization={handleOnAgreePrivacyAuthorization}
>
我同意
</Button>
组件封装
代码示例在官网文档已提供了demo。拿来修改下,改成React组件,效果图如下
下面贴下弹窗代码,只需要在页面中引入即可。
jsx
jsx
import React, { memo, useEffect, useState } from 'react';
import { View, Text, Button } from '@tarojs/components';
import './index.less';
let privacyHandler;
let privacyResolves = new Set<Function>();
if (wx?.onNeedPrivacyAuthorization) {
wx?.onNeedPrivacyAuthorization(resolve => {
privacyHandler?.(resolve);
});
}
const PrivacyPopup: React.FC = () => {
const [visible, setVisible] = useState<boolean>(false);
const handleDisagree = () => {
setVisible(false);
privacyResolves.forEach(resolve => {
resolve({
event: 'disagree'
});
});
privacyResolves.clear();
};
const handleAgree = () => {
setVisible(false);
// 额外处理:例如有可能弹出授权弹窗,但是用户没点击 同意/拒绝 就回退页面,导致没有resolve,这里让所有pending中的wx隐私接口继续执行
privacyResolves.forEach(resolve => {
resolve({
event: 'agree',
buttonId: 'agree-btn'
});
});
privacyResolves.clear();
};
useEffect(() => {
privacyHandler = resolve => {
privacyResolves.add(resolve);
setVisible(true);
};
}, []);
const openPrivacyContract = () => {
wx?.openPrivacyContract({
success: () => {
console.log('openPrivacyContract success');
},
fail: res => {
console.error('openPrivacyContract fail', res);
}
});
};
return (
<View className="pp-container">
{visible && <View className="pp-mask" />}
{visible && (
<View className="pp-wrap">
<View className="pp-head">
<View className="pp-head-title">用户隐私保护提示</View>
</View>
<View className="pp-content">
感谢您使用xxx服务,您在使用该服务前,请仔细阅读
<Text className="pp-link" onClick={openPrivacyContract}>
{' '}
用户隐私保护指引{' '}
</Text>
。当您点击同意并开始使用该服务时,即表示您已理解并同意该条款内容。
</View>
<View className="pp-footer">
<Button id="disagree-btn" className="pp-btn pp-btn_refuse" onClick={handleDisagree}>
拒绝
</Button>
<Button
id="agree-btn"
className="pp-btn"
openType="agreePrivacyAuthorization"
onAgreePrivacyAuthorization={handleAgree}
>
同意
</Button>
</View>
<View className="pp-safearea" />
</View>
)}
</View>
);
};
export default memo(PrivacyPopup);
样式
less
.pp {
&-container {
position: fixed;
z-index: 10;
}
&-mask {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
animation: ppFadeIn 0.2s cubic-bezier(0.55, 0, 0.55, 0.2) forwards;
background: rgba(4, 8, 18, 0.4);
}
&-wrap {
max-height: 80vh;
display: flex;
flex-direction: column;
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 36px 36px 0;
border-radius: 40px 40px 0 0;
animation: ppSlideUpIn 0.2s cubic-bezier(0.55, 0, 0.55, 0.2);
background: #fff;
}
&-head {
flex-shrink: 0;
&-title {
font-weight: 500;
font-size: 40px;
line-height: 48px;
color: #1f242e;
}
}
&-link {
color: #3098ff;
}
&-content {
margin: 28px 0;
font-size: 32px;
line-height: 48px;
color: #4d535c;
}
&-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 0;
}
&-btn {
display: block;
width: 280px;
padding: 28px;
border: none;
box-shadow: 0 4px 16px rgba(46, 87, 234, 0.2);
background: radial-gradient(
94.86% 3821.12% at 91.96% 50%,
rgba(63, 220, 255, 0.38) 0%,
rgba(26, 241, 255, 0) 100%
),
linear-gradient(262.71deg, #3098ff 5.67%, #3c70ff 94.33%);
border-radius: 16px;
color: #fff;
font-size: 36px;
line-height: 42px;
font-weight: 600;
box-sizing: border-box;
margin: 0 auto;
&::after {
border: none;
}
&_refuse {
background: #fff;
border: 1px solid #e9e9e9;
color: #4d535c;
box-shadow: none;
}
}
&-safearea {
display: block;
width: 100%;
padding-bottom: env(safe-area-inset-bottom);
}
@keyframes ppSlideUpIn {
0% {
transform: translate(0, 100%);
}
100% {
transform: translate(0, 0);
}
}
@keyframes ppFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
官方演示的弹窗没有蒙层,弹窗显示时可以随意点击页面,所以官方演示代码里做了弹窗保持单例的逻辑。上面的封装的组件有蒙层,弹窗关闭前不能点击其他,就不做这处理了。
调试
代码写好后,开始调试。我们先去微信后台的 设置-基本设置-服务内容声明-用户隐私保护指引 填写你的小程序用到的隐私接口,例如
Taro项目中 app.config.ts
加上 __usePrivacyCheck__: true
。
另外,微信开发者工具的调试基础库最好设置成3.0.0
。
然后在需要的页面引入组件即可。
其他
最近微信又更新了,隐私同意按钮支持与手机号快速验证组件、手机号实时验证组件耦合使用。
html
<button id="agree-btn1" open-type="getPhoneNumber|agreePrivacyAuthorization" bindgetphonenumber="handleGetPhoneNumber" bindagreeprivacyauthorization="handleAgreePrivacyAuthorization">同意隐私协议并授权手机号</button>
<button id="agree-btn2" open-type="getRealtimePhoneNumber|agreePrivacyAuthorization" bindgetrealtimephonenumber="handleGetRealtimePhoneNumber" bindagreeprivacyauthorization="handleAgreePrivacyAuthorization">同意隐私协议并授权手机号</button>
<button id="agree-btn3" open-type="getUserInfo|agreePrivacyAuthorization" bindgetuserinfo="handleGetUserInfo" bindagreeprivacyauthorization="handleAgreePrivacyAuthorization">同意隐私协议并获取头像昵称信息</button>
就是说获取手机号和同意隐私同一个按钮,点同意后即可调通隐私接口。
我们可以另外封装个弹窗组件
jsx
import React, { memo } from 'react';
import { View, Text, Button } from '@tarojs/components';
import './index.less';
interface PrivacyPopupProps {
visible: boolean;
onClose: () => void;
}
const PrivacyUserPopup: React.FC<PrivacyPopupProps> = ({ visible, onClose }) => {
const handleGetPhone = (e: any) => {
onClose();
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
return;
}
// 下面写登录等逻辑
};
const handleAgree = () => {
console.log('同意');
};
const openPrivacyContract = () => {
wx?.openPrivacyContract({
success: () => {
console.log('openPrivacyContract success');
},
fail: res => {
console.error('openPrivacyContract fail', res);
}
});
};
return (
<View className="pp-container">
{visible && <View className="pp-mask" />}
{visible && (
<View className="pp-wrap">
<View className="pp-head">
<View className="pp-head-title">用户隐私保护提示</View>
</View>
<View className="pp-content">
感谢您使用xxx服务,您在使用该服务前,请仔细阅读
<Text className="pp-link" onClick={openPrivacyContract}>
{' '}
用户隐私保护指引{' '}
</Text>
。当您点击同意并开始使用该服务时,即表示您已理解并同意该条款内容。
</View>
<View className="pp-footer">
<Button id="disagree-btn" className="pp-btn pp-btn_refuse" onClick={onClose}>
拒绝
</Button>
<Button
id="agree-btn"
className="pp-btn"
// @ts-ignore
openType="getPhoneNumber|agreePrivacyAuthorization"
onAgreePrivacyAuthorization={handleAgree}
onGetPhoneNumber={handleGetPhone}
>
同意隐私协议并授权手机号
</Button>
</View>
<View className="pp-safearea" />
</View>
)}
</View>
);
};
export default memo(PrivacyUserPopup);
效果图如下,注意调试时 开发者工具基础库要 3.0.1
如果你的小程序只需要获取手机号/用户信息的隐私接口,或者说只有获取手机号/用户信息后才会用到其他隐私接口,那么用这个弹窗最好了。
但是作者开发的小程序还涉及粘贴板、获取位置信息等隐私接口,假设用户先使用了粘贴板隐私接口,还是得用第一个用法才能隐私授权。所以懒得分开处理,为方便统一,还是用第一个弹窗组件吧。