Przeglądaj źródła

feat: 增强隐私政策同意管理与TabBar控制

在多个组件中集成了隐私政策同意管理功能,新增了控制TabBar显示状态的方法。更新了登录失败后的处理逻辑,确保在用户未同意政策时阻止TabBar的切换。同时,优化了政策弹窗的显示逻辑,确保用户体验流畅。
ylong 2 tygodni temu
rodzic
commit
ee06107e21

+ 19 - 8
api/config.js

@@ -1,5 +1,22 @@
 import ENV_CONFIG from "@/.env.js";
 import { silentLogin } from "./auth";
+import { HOME_TAB_PATH } from "@/utils/policy-config.js";
+import { isPolicyConsented } from "@/utils/policy-consent-manager.js";
+
+function redirectToHomeTab() {
+  const app = getApp();
+  // 协议弹窗未关闭、或未同意协议时,禁止跳转(避免从启动页被拉到卖书 Tab)
+  if (app && app.globalData && app.globalData.needPolicyConsent) {
+    return;
+  }
+  if (!isPolicyConsented()) {
+    return;
+  }
+  uni.switchTab({
+    url: HOME_TAB_PATH,
+    fail: () => {},
+  });
+}
 // api前缀
 export const HTTP_URL_DEV_PREFIX = "/api";
 export const HTTP_URL_PROD_PREFIX = "/api";
@@ -71,10 +88,7 @@ export const httpResponse = (res) => {
             uni.$u.http.request(res.config).then(resolve).catch(reject);
           })
           .catch(() => {
-            // 登录失败,跳转首页
-            uni.switchTab({
-              url: "/pages/home/index",
-            });
+            redirectToHomeTab();
             reject(res.data);
           });
         return resolve(res.data);
@@ -89,10 +103,7 @@ export const httpResponse = (res) => {
           uni.$u.http.request(res.config).then(resolve).catch(reject);
         })
         .catch(() => {
-          // 登录失败,跳转首页
-          uni.switchTab({
-            url: "/pages/home/index",
-          });
+          redirectToHomeTab();
           reject(res.data);
         });
       return resolve(res.data);

+ 2 - 2
api/upload.js

@@ -53,7 +53,7 @@ export const httpResponse = (res) => {
 				// 未登录
 				uni.removeStorageSync('token');
 				uni.switchTab({
-					url: '/pages/home/index'
+					url: '/pages/sell/index'
 				})
 				return reject(res.data);
 			}
@@ -63,7 +63,7 @@ export const httpResponse = (res) => {
 			console.log('未登录>>>>');
 			// 未登录
 			uni.switchTab({
-				url: '/pages/home/index'
+				url: '/pages/sell/index'
 			})
 			uni.removeStorageSync('token');
 			return reject(res.data);

+ 46 - 4
components/policy-consent-host.vue

@@ -1,8 +1,15 @@
 <template>
     <view class="policy-consent-host">
-        <policy-consent-modal ref="policyModal" @accepted="onPolicyAccepted" />
+        <policy-consent-modal
+            ref="policyModal"
+            @accepted="onPolicyAccepted"
+            @visibility-change="onPolicyModalVisibility"
+        />
         <!-- #ifdef MP-WEIXIN -->
-        <privacy-authorize-popup ref="privacyPopup" />
+        <privacy-authorize-popup
+            ref="privacyPopup"
+            @visibility-change="onPrivacyModalVisibility"
+        />
         <!-- #endif -->
     </view>
 </template>
@@ -11,8 +18,10 @@
 import PolicyConsentModal from '@/components/policy-consent-modal.vue'
 import { eventBus } from '@/utils/event-bus'
 import {
+    markHomeRefreshAfterPolicy,
     registerPolicyHost,
     setPolicyConsented,
+    setTabbarBlockedState,
     unregisterPolicyHost,
 } from '@/utils/policy-consent-manager.js'
 
@@ -27,25 +36,57 @@ export default {
         PrivacyAuthorizePopup,
         // #endif
     },
+    data() {
+        return {
+            policyModalVisible: false,
+            privacyModalVisible: false,
+        }
+    },
+    computed: {
+        tabbarBlocked() {
+            return this.policyModalVisible || this.privacyModalVisible
+        },
+    },
+    watch: {
+        tabbarBlocked: {
+            immediate: true,
+            handler(val) {
+                setTabbarBlockedState(val)
+            },
+        },
+    },
     mounted() {
         registerPolicyHost(this)
         this.tryOpenPolicyModal()
     },
     beforeDestroy() {
+        setTabbarBlockedState(false)
         unregisterPolicyHost(this)
     },
     methods: {
+        onPolicyModalVisibility(visible) {
+            this.policyModalVisible = visible
+        },
+        onPrivacyModalVisibility(visible) {
+            this.privacyModalVisible = visible
+        },
         tryOpenPolicyModal() {
             const app = getApp()
             if (!app || !app.globalData || !app.globalData.needPolicyConsent) return
             this.$nextTick(() => {
-                this.$refs.policyModal && this.$refs.policyModal.open()
+                if (this.$refs.policyModal) {
+                    setTabbarBlockedState(true)
+                    this.$refs.policyModal.open()
+                }
             })
         },
         tryOpenPrivacyPopup() {
             // #ifdef MP-WEIXIN
             this.$nextTick(() => {
-                this.$refs.privacyPopup && this.$refs.privacyPopup.open()
+                if (this.$refs.privacyPopup) {
+                    setTabbarBlockedState(true)
+                    this.$refs.privacyPopup.open()
+                }
             })
             // #endif
         },
@@ -55,6 +96,7 @@ export default {
             if (app && app.globalData) {
                 app.globalData.needPolicyConsent = false
             }
+            markHomeRefreshAfterPolicy()
             eventBus.emit('policyAccepted')
             if (app && typeof app.runPendingLaunchLogin === 'function') {
                 app.runPendingLaunchLogin()

+ 7 - 1
components/policy-consent-modal.vue

@@ -31,7 +31,6 @@
 
 <script>
 import { ARTICLE_CODE, ARTICLE_TITLE, POLICY_POPUP_Z_INDEX } from '@/utils/policy-config.js'
-
 export default {
     data() {
         return {
@@ -40,10 +39,17 @@ export default {
             policyZIndex: POLICY_POPUP_Z_INDEX,
         }
     },
+    watch: {
+        visible(val) {
+            this.$emit('visibility-change', val)
+        },
+    },
     methods: {
         open() {
             this.checked = false
             this.visible = true
+            // 同步触发 visibility,确保立即 hideTabBar
+            this.$emit('visibility-change', true)
         },
         close() {
             this.visible = false

+ 6 - 1
components/privacy-authorize-popup.vue

@@ -36,7 +36,6 @@ import {
     getPrivacyResolve,
 } from '@/utils/policy-consent-manager.js'
 import { PRIVACY_POPUP_Z_INDEX } from '@/utils/policy-config.js'
-
 export default {
     data() {
         return {
@@ -44,9 +43,15 @@ export default {
             privacyZIndex: PRIVACY_POPUP_Z_INDEX,
         }
     },
+    watch: {
+        visible(val) {
+            this.$emit('visibility-change', val)
+        },
+    },
     methods: {
         open() {
             this.visible = true
+            this.$emit('visibility-change', true)
         },
         close() {
             this.visible = false

+ 6 - 0
pages-sell/components/sell-container/index.vue

@@ -264,6 +264,12 @@ export default {
 		this.getHotSearchData()
 	},
 	methods: {
+		/** 协议同意且登录完成后刷新首页数据(首次进入时 created 可能因未登录导致数据不全) */
+		refreshPageData() {
+			this.hasShareList()
+			this.getIndexCateInfo()
+			this.getHotSearchData()
+		},
 		async getHotSearchData() {
 			try {
 				const res = await this.$u.api.getHotSearchListAjax();

+ 2 - 0
pages/cart/index.vue

@@ -7,8 +7,10 @@
 
 <script>
 import PolicyConsentHost from '@/components/policy-consent-host.vue'
+import tabbarBlockMixin from '@/utils/tabbar-block-mixin.js'
 
     export default {
+        mixins: [tabbarBlockMixin],
         components: {
             PolicyConsentHost,
         },

+ 2 - 0
pages/home/index.vue

@@ -137,11 +137,13 @@
     import UpsellQrcode from "./components/upsell-qrcode.vue";
     import ActivityHost from "@/components/activity-host.vue";
     import PolicyConsentHost from "@/components/policy-consent-host.vue";
+    import tabbarBlockMixin from "@/utils/tabbar-block-mixin.js";
     import FloatingDrag from "@/components/floating-drag.vue";
 
     const app = getApp();
 
 export default {
+    mixins: [tabbarBlockMixin],
     components: {
         notScanned,
         InputIsbn,

+ 2 - 0
pages/mine/index.vue

@@ -123,9 +123,11 @@
 	import WithdrawalConfirm from '../../components/withdrawal-confirm.vue';
 	import floatingActivity from '../../components/floating-activity.vue';
 	import PolicyConsentHost from '@/components/policy-consent-host.vue';
+	import tabbarBlockMixin from '@/utils/tabbar-block-mixin.js';
 	import { buildAgreementPageUrl } from '@/utils/policy-config.js';
 
 	export default {
+		mixins: [tabbarBlockMixin],
 		components: {
 			WithdrawalProgress,
 			WithdrawalConfirm,

+ 48 - 1
pages/sell/index.vue

@@ -9,19 +9,30 @@
 <script>
 import ActivityHost from "@/components/activity-host.vue";
 import PolicyConsentHost from "@/components/policy-consent-host.vue";
+import tabbarBlockMixin from "@/utils/tabbar-block-mixin.js";
+import { eventBus } from "@/utils/event-bus";
+import { consumeHomeRefreshAfterPolicy } from "@/utils/policy-consent-manager.js";
 
 export default {
+	mixins: [tabbarBlockMixin],
 	components: {
 		ActivityHost,
 		PolicyConsentHost,
 	},
 	data() {
 		return {
-			pendingActivityOptions: {}
+			pendingActivityOptions: {},
+			pendingPolicyRefresh: false,
+			policyAcceptedHandler: null,
+			loginSuccessHandler: null,
 		}
 	},
 	onLoad(options) {
 		this.pendingActivityOptions = options || {};
+		this.bindPolicyRefreshListeners();
+	},
+	onUnload() {
+		this.unbindPolicyRefreshListeners();
 	},
 	onReady() {
 		this.$nextTick(() => {
@@ -33,6 +44,10 @@ export default {
 	},
 	onShow() {
 		this.$updateCartBadge();
+		// 在其他 Tab 同意协议后切回首页时补刷数据
+		if (consumeHomeRefreshAfterPolicy() && uni.getStorageSync("token")) {
+			this.refreshHomePageData();
+		}
 	},
 	onShareAppMessage(res) {
 		return this.$refs.activityHost?.getShareAppMessage(res) || {
@@ -61,6 +76,38 @@ export default {
 		comp.scrollTop = e.scrollTop;
 	},
 	methods: {
+		bindPolicyRefreshListeners() {
+			if (!this.policyAcceptedHandler) {
+				this.policyAcceptedHandler = () => {
+					this.pendingPolicyRefresh = true;
+				};
+				eventBus.on("policyAccepted", this.policyAcceptedHandler);
+			}
+			if (!this.loginSuccessHandler) {
+				this.loginSuccessHandler = () => {
+					if (!this.pendingPolicyRefresh) return;
+					this.pendingPolicyRefresh = false;
+					consumeHomeRefreshAfterPolicy();
+					this.refreshHomePageData();
+				};
+				eventBus.on("loginSuccess", this.loginSuccessHandler);
+			}
+		},
+		unbindPolicyRefreshListeners() {
+			if (this.policyAcceptedHandler) {
+				eventBus.off("policyAccepted", this.policyAcceptedHandler);
+				this.policyAcceptedHandler = null;
+			}
+			if (this.loginSuccessHandler) {
+				eventBus.off("loginSuccess", this.loginSuccessHandler);
+				this.loginSuccessHandler = null;
+			}
+		},
+		refreshHomePageData() {
+			this.$nextTick(() => {
+				this.$refs.sellContainer?.refreshPageData?.();
+			});
+		},
 		hasActivityEntryOptions(options = {}) {
 			return !!(options.scene || options.reduceCode || options.upsellCode);
 		}

+ 3 - 0
utils/policy-config.js

@@ -7,6 +7,9 @@ export const STORAGE_KEY_POLICY_CONSENT = 'policyConsentVersion'
 export const POLICY_POPUP_Z_INDEX = 10100
 export const PRIVACY_POPUP_Z_INDEX = 10110
 
+/** TabBar「首页」对应页面(买书商城),勿与 pages/home/index(卖书)混淆 */
+export const HOME_TAB_PATH = '/pages/sell/index'
+
 /** CMS 文章 code */
 export const ARTICLE_CODE = {
     userAgreement: 'userAgreement',

+ 55 - 0
utils/policy-consent-manager.js

@@ -2,9 +2,64 @@ import {
     POLICY_CONSENT_VERSION,
     STORAGE_KEY_POLICY_CONSENT,
 } from '@/utils/policy-config.js'
+import { eventBus } from '@/utils/event-bus'
 
 let policyHostVm = null
 let privacyResolve = null
+let lastTabbarBlocked = false
+let pendingHomeRefreshAfterPolicy = false
+
+/** 用户刚同意协议,首页需在登录后补拉数据 */
+export function markHomeRefreshAfterPolicy() {
+    pendingHomeRefreshAfterPolicy = true
+}
+
+export function consumeHomeRefreshAfterPolicy() {
+    const need = pendingHomeRefreshAfterPolicy
+    pendingHomeRefreshAfterPolicy = false
+    return need
+}
+
+function hideNativeTabBar() {
+    uni.hideTabBar({
+        animation: false,
+        fail: () => {},
+    })
+}
+
+function showNativeTabBar() {
+    uni.showTabBar({
+        animation: false,
+        fail: () => {},
+    })
+}
+
+/**
+ * 协议/隐私弹窗期间隐藏原生 tabBar(小程序内 cover-view 无法可靠拦截 tabBar 点击)
+ */
+export function setTabbarBlockedState(blocked) {
+    const next = !!blocked
+    lastTabbarBlocked = next
+    if (next) {
+        hideNativeTabBar()
+    } else {
+        showNativeTabBar()
+    }
+    eventBus.emit('tabbarBlockChange', next)
+}
+
+/** Tab 切换回显时重新应用 tabBar 显隐 */
+export function syncTabbarBlockedState() {
+    if (lastTabbarBlocked) {
+        hideNativeTabBar()
+    } else {
+        showNativeTabBar()
+    }
+}
+
+export function getTabbarBlockedState() {
+    return lastTabbarBlocked
+}
 
 export function isPolicyConsented() {
     return uni.getStorageSync(STORAGE_KEY_POLICY_CONSENT) === POLICY_CONSENT_VERSION

+ 8 - 0
utils/tabbar-block-mixin.js

@@ -0,0 +1,8 @@
+import { syncTabbarBlockedState } from '@/utils/policy-consent-manager.js'
+
+/** Tab 页混入:切换 Tab 时重新同步 tabBar 显隐(配合协议/隐私弹窗 hideTabBar) */
+export default {
+    onShow() {
+        syncTabbarBlockedState()
+    },
+}