From 114c3d78ff8be7ceb9880ab236e81c24ca6d256f Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Sat, 25 Apr 2026 10:33:45 +0800
Subject: [PATCH] feat: 配置微信小程序隐私协议

---
 src/pages/login/index.vue     |   31 +++++++
 src/manifest.json             |    3 
 src/components/GetPrivacy.vue |  199 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 231 insertions(+), 2 deletions(-)

diff --git a/src/components/GetPrivacy.vue b/src/components/GetPrivacy.vue
new file mode 100644
index 0000000..8ab4609
--- /dev/null
+++ b/src/components/GetPrivacy.vue
@@ -0,0 +1,199 @@
+<template>
+  <view v-if="showPrivacy" :class="privacyClass">
+    <view :class="contentClass">
+      <view class="title">用户隐私保护指引</view>
+      <view class="des">
+        感谢您选择使用掌控智飞小程序,我们非常重视您的个人信息安全和隐私保护。使用我们的产品前,请您仔细阅读“
+        <text class="link" @tap="openPrivacyContract">{{privacyContractName}} </text>”,
+        如您同意此隐私保护指引,请点击同意按钮,开始使用此小程序,我们将尽全力保护您的个人信息及合法权益,感谢您的信任!<br />
+      </view>
+      <view class="btns">
+        <button class="item reject" @click="exitMiniProgram">拒绝</button>
+        <button id="agree-btn" class="item agree" open-type="agreePrivacyAuthorization"
+                @agreeprivacyauthorization="handleAgreePrivacyAuthorization">同意</button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue';
+
+const props = defineProps({
+  position: {
+    type: String,
+    default: 'center'
+  }
+});
+
+const emit = defineEmits(['allowPrivacy']);
+
+const isRead = ref(false);
+const showPrivacy = ref(false);
+const privacyContractName = ref('');
+const resolvePrivacyAuthorization = ref(null);
+
+const privacyClass = computed(() => {
+  return props.position === 'bottom' ? 'privacy privacy-bottom' : 'privacy';
+});
+
+const contentClass = computed(() => {
+  return props.position === 'bottom' ? 'content content-bottom' : 'content';
+});
+
+onMounted(() => {
+  if (uni.onNeedPrivacyAuthorization) {
+    uni.onNeedPrivacyAuthorization((resolve) => {
+      resolvePrivacyAuthorization.value = resolve;
+    });
+  }
+
+  if (uni.getPrivacySetting) {
+    uni.getPrivacySetting({
+      success: (res) => {
+        if (res.needAuthorization) {
+          privacyContractName.value = res.privacyContractName;
+          showPrivacy.value = true;
+        } else {
+          console.log('已经同意隐私授权,不需要再次授权')
+          showPrivacy.value = false;
+        }
+      },
+    });
+  }
+});
+
+const openPrivacyContract = () => {
+  uni.openPrivacyContract({
+    success: () => {
+      isRead.value = true;
+    },
+    fail: () => {
+      uni.showToast({
+        title: '遇到错误',
+        icon: 'error',
+      });
+    },
+  });
+};
+
+const exitMiniProgram = () => {
+  wx.exitMiniProgram();
+};
+
+const handleAgreePrivacyAuthorization = () => {
+  showPrivacy.value = false;
+  emit('allowPrivacy');
+  if (typeof resolvePrivacyAuthorization.value === 'function') {
+    resolvePrivacyAuthorization.value({
+      buttonId: 'agree-btn',
+      event: 'agree',
+    });
+  }
+};
+
+const closePrivacy = () => {
+  showPrivacy.value = false;
+};
+
+// 暴露给父组件使用
+defineExpose({
+  closePrivacy
+});
+</script>
+
+<style scoped>
+.privacy {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  background: rgba(0, 0, 0, .5);
+  z-index: 9999999;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.privacy-bottom {
+  align-items: flex-end;
+}
+
+.content {
+  width: 632rpx;
+  padding: 48rpx;
+  box-sizing: border-box;
+  background: #fff;
+  border-radius: 16rpx;
+}
+
+.content-bottom {
+  position: absolute;
+  bottom: 0;
+  width: 96%;
+  padding: 36rpx;
+  border-radius: 16rpx 16rpx 0 0;
+}
+
+.content .title {
+  text-align: center;
+  color: #333;
+  font-weight: bold;
+  font-size: 32rpx;
+}
+
+.content .des {
+  font-size: 26rpx;
+  color: #666;
+  margin-top: 40rpx;
+  text-align: justify;
+  line-height: 1.6;
+}
+
+.content .des .link {
+  color: #1989ff;
+  text-decoration: underline;
+}
+
+.btns {
+  margin-top: 48rpx;
+  margin-bottom: 12rpx;
+  display: flex;
+}
+
+.btns .item {
+  width: 200rpx;
+  height: 72rpx;
+  overflow: visible;
+  display: flex;
+  align-items: center;
+
+  justify-content: center;
+  /* border-radius: 16rpx; */
+  box-sizing: border-box;
+  border: none !important;
+}
+
+.btns .reject {
+  background: #f4f4f5;
+  color: #1989ff;
+  font-size: 14px;
+  background: #edf5fe;
+  font-weight: 300;
+  margin-right: 16rpx;
+}
+
+.btns .agree {
+  width: 200rpx;
+  background: #1989ff;
+  color: #fff;
+  font-size: 16px;
+
+}
+
+.privacy-bottom .btns .agree {
+  width: 440rpx;
+
+}
+</style>
diff --git a/src/manifest.json b/src/manifest.json
index 8e1964b..4ae1e30 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -79,7 +79,8 @@
         "setting" : {
             "urlCheck" : false
         },
-        "usingComponents" : true
+        "usingComponents" : true,
+        "__usePrivacyCheck__" : true
     },
     "mp-alipay" : {
         "usingComponents" : true
diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue
index 318b8a6..6964069 100644
--- a/src/pages/login/index.vue
+++ b/src/pages/login/index.vue
@@ -30,6 +30,10 @@
 
     <button class="login-btn" :style="[inputStyle]" @tap="submit">登录</button>
     <image class="lowerRightCorner" :src="droneSvg" />
+
+    <!-- #ifdef MP-WEIXIN -->
+    <GetPrivacy ref="privacyComponent" position="center" @allowPrivacy="allowPrivacy"/>
+    <!-- #endif -->
   </view>
 </template>
 
@@ -38,7 +42,9 @@
 import md5 from "js-md5";
 import { loginByUsername } from "@/api/user/index.js";
 import { useUserStore } from "@/store/index.js";
-
+// #ifdef MP-WEIXIN
+import GetPrivacy from '@/components/GetPrivacy.vue'
+// #endif
 import { HOME_PATH, LOGIN_PATH, removeQueryString } from "@/router";
 import { onMounted } from "vue";
 
@@ -147,6 +153,29 @@
     redirect = decodeURIComponent(options.redirect);
   }
 });
+
+// #ifdef MP-WEIXIN
+const allowPrivacy = () =>{
+
+  // 同意隐私协议触发事件,有些接口需要同意授权后才能执行,比如获取手机号授权接口,可以在同意隐私协议后,再执行授权获取手机号接口,如果不需要可以不添加该方法
+  console.log('同意隐私授权')
+}
+const privacyComponent =ref(null)
+onShow(() =>{
+  wx.getPrivacySetting({
+    success: res => {
+      console.log(res)
+      if (!res.needAuthorization) {
+        privacyComponent?.value?.closePrivacy()
+        // 查询授权,针对有tab切换的页面,可以在onshow中查询隐私授权状态,判断在tab切换后是否需要关闭授权弹框
+        console.log('已经同意隐私授权,不需要再次授权')
+      }
+    },
+    fail: () => {},
+    complete: () => {}
+  })
+})
+// #endif
 </script>
 
 <style lang="scss" scoped>

--
Gitblit v1.9.3