English
Privacy Statement Confirmation Popup on the First Screen
About 1368 wordsAbout 5 min
2025-04-28
When enterprise employees open an internal application for the first time, common requirements include:
- Automatically displaying a privacy statement popup when the app starts
- Requiring employees to check all statements before they can continue using the app
- Recording the confirmation status to avoid repeated popups
When an employee logs in to the enterprise app for the first time, the first screen automatically shows a popup containing the privacy statement and supplementary terms. The employee can enter the app only after checking all terms and clicking "Confirm". If they do not check all terms or click "Cancel", they cannot use the app. This ensures that employees clearly understand and agree to the relevant privacy policies.
Applicable Scenario
This solution is suitable for scenarios where users must be required to confirm a privacy statement, supplementary terms, or compliance agreements when the app is launched for the first time.
Solution
Core Objectives
- Automatically display a privacy statement popup the first time the app is opened
- Prevent confirmation unless all terms are checked
- Exit the app when the user clicks Cancel or the back button
- Record the confirmation status after successful confirmation so the popup is not shown again
Technical Architecture
Implementation Flow
- Trigger a forced-notification check when the app starts.
- The server returns the popup component configuration that needs to be displayed.
- The page displays the privacy statement and supplementary terms.
- The user checks all terms and clicks "Confirm".
- The app records the confirmation status locally and returns a successful result.
- If the user clicks "Cancel" or presses the back button, the app exits.
Build the base structure and styles
Use WXML to build the popup layout, define styles in WXSS, and initialize data in JS to create the privacy statement display framework.
index.wxml<view class="popup-container"> <!-- Title area --> <view class="title">Privacy Policy Confirmation</view> <!-- Prompt text --> <view class="text">Please read and check all statements below, then click Confirm to continue using the application</view> <!-- Statement checkbox area --> <view class="weui-cells weui-cells_after-title"> <checkbox-group bindchange="checkboxChange"> <label class="weui-cell weui-check__label" wx:for="{{items}}" wx:key="{{item.value}}"> <view class="weui-cell__hd"> <checkbox value="{{item.value}}" checked="{{item.checked}}"/> </view> <view class="weui-cell__bd"> <view class="statement-name">{{item.name}}</view> <view class="statement-link">{{item.link}}</view> </view> </label> </checkbox-group> </view> <!-- Action button area --> <view class="btns-wrap"> <button class="mini-btn primary" type="primary" size="mini" bindtap="sure">Confirm</button> <button class="mini-btn default" type="default" size="mini" bindtap="cancel">Cancel</button> </view> </view>index.wxss/* Popup container */ .popup-container { padding: 15px; box-sizing: border-box; } /* Title style */ .title { font-size: 24px; font-weight: bold; text-align: center; line-height: 50px; margin-bottom: 10px; } /* Prompt text */ .text { padding: 0 12px; color: #666; margin-bottom: 20px; } /* Checkbox area styles */ .weui-cells { padding: 0 12px; } .weui-cell { display: flex; align-items: center; padding: 10px 0; border-bottom: 1px solid #eee; } .weui-cell__hd { margin-right: 10px; } .statement-name { font-weight: 500; } .statement-link { font-size: 14px; color: #007aff; margin-top: 4px; } /* Button area */ .btns-wrap { display: flex; justify-content: center; margin-top: 30px; } .mini-btn { width: 120px; margin: 0 8px; }index.jsComponent({ // Component data definition data: { // Privacy statement list (supports dynamic extension) items: [ { value: 'privacy', name: 'Privacy Policy', link: 'https://m.fxiaoke.com/privacy', // Replace with the real link in actual projects checked: false }, { value: 'privacyExtend', name: 'Supplementary Privacy Terms', link: 'https://m.fxiaoke.com/privacy/extend', // Replace with the real link in actual projects checked: false } ] }, // Other configurations... })Handle checkbox interaction
Implement checkbox state synchronization to ensure the user's actions are reflected in component data in real time.
Component({ methods: { /** * Handle checkbox state changes * @param {Object} e - Event object containing the list of selected values */ checkboxChange(e) { const selectedValues = e.detail.value; // Get all currently selected values const updatedItems = this.data.items.map(item => ({ ...item, // Check whether the current statement is selected checked: selectedValues.includes(item.value) })); // Update component data and refresh the view this.setData({ items: updatedItems }); } } })Handle button clicks
Implement the logic for the "Confirm" and "Cancel" buttons, including checkbox validation, status storage, and page navigation.
import fsApi from 'fs-hera-api'; // Import Fxiaoke API Component({ methods: { /** * Confirm button click event * Validate whether all statements are checked, then store the status and enter the app */ sure() { // Filter unchecked statements const unCheckedItems = this.data.items.filter(item => !item.checked); if (unCheckedItems.length > 0) { // There are still unchecked items, prompt the user wx.showToast({ title: 'Please check all privacy statements', icon: 'error', duration: 2000 }); return; } // All statements are checked, save the confirmation status this.saveConfirmationStatus().then(() => { // Hide the popup with animation transition this.setData({ show: false }); // Delay closing the popup page to ensure the animation finishes setTimeout(() => { // Notify the previous page that confirmation succeeded fsApi.page.setCallbackDataThenBack({ forceRemindAgain: false, // Indicates that no reminder is needed again success: true }); }, 200); }); }, /** * Cancel button click event * Exit the app when canceled */ cancel() { wx.shutDownNativeApp(); // Call native API to close the app }, /** * Save the user's confirmation status locally * @returns {Promise} - Promise for the storage operation */ saveConfirmationStatus() { return new Promise((resolve, reject) => { const storageConfig = { name: 'privacy_confirmation_status', // Custom storage key name (it is recommended to follow a project-wide naming convention) value: JSON.stringify({ confirmed: true, timestamp: new Date().getTime() // Record confirmation time }), // Success callback onSuccess: () => resolve(), // Failure callback onFail: (err) => { console.error('Failed to save confirmation status:', err); wx.showToast({ title: 'Operation failed, please try again', icon: 'error', duration: 2000 }); reject(err); } }; // Call Fxiaoke storage API fsApi.jsapi.storageSetItem(storageConfig); }); } } })Handle app back navigation
import fsApi from 'fs-hera-api'; Component({ lifetimes:{ attached() { if (wx.onBackPressed) { wx.onBackPressed({ fn: this.onBackPressed.bind(this), pageid: this.getPageId() }); } ... } }, methods: { ... onBackPressed() { wx.shutDownNativeApp(); return true; }, ... } })
Key Points
Popup Data Structure
It is recommended to abstract each privacy statement into a unified object containing at least:
value: Unique identifiername: Display namelink: Link to the clausechecked: Whether it has been checked
Confirmation Logic
- All terms must be checked before confirmation is allowed
- After successful confirmation, it is recommended to record both the confirmation status and the confirmation time
Exit Logic
- Exit the app when the user clicks "Cancel"
- Exit the app when the user presses the system back button
- Prevent users from bypassing the privacy confirmation process and entering the app
Local State Storage
- It is recommended to use a consistently named local storage key
- It is recommended that the stored content include the confirmation status and a timestamp
- If storage fails, a prompt should be shown and the user should be prevented from entering the app
Complete Code
Component Example
components
statement
index.js
index.json
index.wxml
index.wxss
app.json
config.json
project.config.json
sitemap.json
components/statement/index.js
Component({
data: {
items: [
{
value: 'privacy',
name: '隐私声明',
link: 'https://m.fxiaoke.com'
},
{
value: 'privacyExtend',
name: '补充隐私声明',
link: 'https://m.fxiaoke.com'
}
]
},
methods:{
onTap() {
wx.showToast({
title: '成功',
duration: 2000
});
},
checkboxChange(e) {
const items = this.data.items
const values = e.detail.value
for (let i = 0, lenI = items.length; i < lenI; ++i) {
items[i].checked = false
for (let j = 0, lenJ = values.length; j < lenJ; ++j) {
if (items[i].value === values[j]) {
items[i].checked = true
break
}
}
}
this.setData({
items
})
},
sure(){
const noCheckItems = this.data.items.filter(item => !item.checked);
if (noCheckItems.length){
wx.showToast({
title: '需勾选所有的隐私声明才可确认',
icon: 'error'
});
}else {
this.avaStorageSetItem().then(() => {
// 先隐藏弹窗,再关闭页面,可隐藏弹窗动画
this.setData({
show: false
});
setTimeout(() => {
fsApi.page.setCallbackDataThenBack({
forceRemindAgain: false,
success: true
});
}, 200);
});
}
},
cancel(){
wx.shutDownNativeApp();
},
// 存储至本地
avaStorageSetItem() {
return new Promise(resolve => {
const num = 1 + this.data.num
let config = {
name: 'avatar_testxx_num1', // 此处需要自定义key
value: JSON.stringify({value: true}) // IOS只能存储字符串,860提供相关的api隐藏该部分
}
config.onSuccess = function () {
resolve();
}
config.onFail = function () {
console.log('set fail',num)
wx.showToast({
title: '失败',
icon: 'error',
duration: 2000
})
}
fsApi.jsapi.storageSetItem(config)
})
},
// 从本地存储获取值
avaStorageGetItem() {
return new Promise((resolve, reject) => {
let self = this
let config = {
name: 'avatar_testxx_num1'
}
config.onSuccess = function (res) {
const data = res?.value ? JSON.parse(res.value) : undefined;
resolve(data?.value);
}
config.onFail = function(res){
console.log('get fail',res)
}
fsApi.jsapi.storageGetItem(config);
})
},
onBackPressed() {
wx.shutDownNativeApp();
return true;
}
},
lifetimes:{
attached() {
if (wx.onBackPressed) {
wx.onBackPressed({
fn: this.onBackPressed.bind(this),
pageid: this.getPageId()
});
}
},
}
});components/statement/index.json
{
"component": true,
"usingComponents": {
"profile": "../profile/index"
}
}components/statement/index.wxml
<view>
<view class="title">通知</view>
<view class="text">请阅读并勾选相关声明后,点击确认,方可正常使用app</view>
<view class="weui-cells weui-cells_after-title">
<checkbox-group bindchange="checkboxChange">
<label class="weui-cell weui-check__label" wx:for="{{items}}" wx:key="{{item.value}}">
<view class="weui-cell__hd">
<checkbox value="{{item.value}}" checked="{{item.checked}}"/>
</view>
<view class="weui-cell__bd">
<view>{{item.name}}</view>
<view>{{item.link}}</view>
</view>
</label>
</checkbox-group>
</view>
<view class="btns-wrap">
<button class="mini-btn" type="primary" size="mini" bindtap="sure">确认</button>
<button class="mini-btn" type="default" size="mini" bindtap="cancel">取消</button>
</view>
</view>components/statement/index.wxss
.title {
font-size: 24px;
font-weight: bold;
text-align: center;
line-height: 50px;
}
.text {
padding: 0 12px;
}
.weui-cells {
padding: 20px;
}
.weui-cell {
display: flex;
margin: 10px 0;
align-items: center;
}
.weui-cell__hd {
margin-right: 10px;
}
.btns-wrap {
display: flex;
justify-content: center;
}
.btns-wrap .mini-btn{
margin: 0 5px;
}app.json
{
"pages": [],
"sitemapLocation": "sitemap.json"
}config.json
{
"components": {
"root": "components/statement/index"
},
"main": ""
}project.config.json
{
"miniprogramAvaId": "fsdemo-lego",
"avaFrameVer": "770.0.4",
"description": "项目配置文件",
"packOptions": {
"ignore": []
},
"setting": {
"urlCheck": true,
"scopeDataCheck": false,
"coverView": true,
"es6": true,
"postcss": true,
"compileHotReLoad": false,
"preloadBackgroundData": false,
"minified": true,
"autoAudits": false,
"newFeature": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"useIsolateContext": true,
"nodeModules": false,
"enhance": false,
"useCompilerModule": true,
"userConfirmedUseCompilerModuleSwitch": false,
"useMultiFrameRuntime": true,
"useApiHook": true,
"useApiHostProcess": true,
"showShadowRootInWxmlPanel": true,
"packNpmManually": false,
"packNpmRelationList": [],
"minifyWXSS": true
},
"compileType": "miniprogram",
"libVersion": "2.15.0",
"appid": "wxb7ca92fe07ce5c66",
"projectname": "legodemo",
"debugOptions": {
"hidedInDevtools": []
},
"scripts": {
"beforeCompile": ""
},
"isGameTourist": false,
"condition": {
"search": {
"list": []
},
"conversation": {
"list": []
},
"game": {
"list": []
},
"plugin": {
"list": []
},
"gamePlugin": {
"list": []
},
"miniprogram": {
"list": []
}
}
}sitemap.json
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [
{
"action": "allow",
"page": "*"
}
]
}Preview
Preview note: The video demonstrates the full interaction flow of the popup, including forced display on first launch, checkbox validation, entering the app after confirmation, and exiting the app when Cancel or Back is used.
FAQ
Q: Why does the popup still appear the next time after confirmation?
Check the following issues:
- Whether local storage was written successfully
- Whether the logic for reading the confirmation status is consistent with the write logic
- Whether the storage key name was changed or conflicts with another key
Q: Why doesn't clicking Back exit the app?
Check the following issues:
- Whether
wx.onBackPressedwas registered correctly - Whether
pageidmatches the current page - Whether the callback function returns
true
Q: Why doesn't clicking Confirm continue into the app?
Check the following issues:
- Whether there are still unchecked terms
- Whether the local storage Promise is resolved correctly
- Whether
setCallbackDataThenBackis called successfully
