refactor(鸿蒙元服务): 适配鸿蒙平台隐私托管与启动屏时序优化
1. 适配鸿蒙元服务平台隐私托管,由系统自动弹出隐私弹窗,应用层不再弹出自建弹窗 2. 新增启动屏strict模式,鸿蒙场景下严格等待首页DIY渲染完成再关闭启动屏,避免白屏 3. 新增splash-state单元测试覆盖新旧模式与鸿蒙场景 4. 清理隐私相关冗余代码与注释,统一平台逻辑分支
This commit is contained in:
@@ -1,4 +1,13 @@
|
||||
<template>
|
||||
<!-- #ifdef MP-HARMONY -->
|
||||
<!--
|
||||
鸿蒙元服务已接入平台隐私托管, 由鸿蒙系统自动弹出系统级隐私弹窗,
|
||||
应用层不再渲染自建 privacy-popup (避免出现"多次弹出隐私弹窗"被驳回)。
|
||||
此处保留空模板, 组件实例仍存在 (供其他平台 share 同一份代码),
|
||||
但模板不会渲染任何内容, 鸿蒙用户看不到自建弹窗。
|
||||
-->
|
||||
<!-- #endif-->
|
||||
<!-- #ifndef MP-HARMONY -->
|
||||
<view>
|
||||
<view class="privacy-mask" v-if="showPop">
|
||||
<view class="privacy-wrap">
|
||||
@@ -6,7 +15,7 @@
|
||||
<view class="privacy-desc">
|
||||
{{ i18n.descPrefix }}
|
||||
<text class="privacy-link" @tap="openPrivacyContract">{{ i18n.privacyContractName }}</text>
|
||||
<!-- #ifdef QUICKAPP-WEBVIEW || H5 || MP-HARMONY -->
|
||||
<!-- #ifdef QUICKAPP-WEBVIEW || H5 -->
|
||||
<text class="privacy-link" @tap="openPrivacyService">{{ i18n.privacyServiceName }}</text>
|
||||
<!-- #endif -->
|
||||
{{ i18n.descSuffix }}
|
||||
@@ -17,13 +26,15 @@
|
||||
<button id="agree-btn" class="privacy-button-btn bg-agree" open-type="agreePrivacyAuthorization"
|
||||
@agreeprivacyauthorization="handleAgree">{{ i18n.agreeBtn }}</button>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 || MP-HARMONY -->
|
||||
<button id="agree-btn" class="privacy-button-btn bg-agree" @tap="handleAgree">{{ i18n.agreeBtn }}</button>
|
||||
<!-- #ifdef H5 -->
|
||||
<button id="agree-btn" class="privacy-button-btn bg-agree" @tap="handleAgree">{{ i18n.agreeBtn
|
||||
}}</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -48,6 +59,12 @@ export default {
|
||||
// 平台提供的隐私协议名称(由 wx.getPrivacySetting 等异步设置)
|
||||
// 弹窗前才参与 i18n.privacyContractName 的填充
|
||||
platformContractName: '',
|
||||
// 平台协议名称就绪 Promise:
|
||||
// created() 中发起异步查询, success/fail 时 resolve
|
||||
// popUp() 中 await 此 Promise 后再调 prepareI18n(), 保证 $lang() 与
|
||||
// platformContractName 都已经准备好, 不会出现"空串被永久缓存"的问题
|
||||
// (与 i18n-lazy mixin 的"boot 完成才能 prepareI18n"是同源契约)
|
||||
platformContractNameReady: null,
|
||||
// 弹窗文案: 模板只读 i18n.xxx
|
||||
// 初值为空串, 弹窗前在 popUp() 中**同步**填充
|
||||
// 此时 App.vue 已 await boot() + await PrivacyManager.show() 完成,
|
||||
@@ -70,10 +87,22 @@ export default {
|
||||
// 所以 listener 可在 created 注册, 但 callback 仍要 nextTick 渲染
|
||||
created() {
|
||||
try {
|
||||
// 创建平台协议名称就绪 Promise
|
||||
// 后续 wx/has/uni.* 三端的异步 API 都会挂到这个 promise 上 resolve,
|
||||
// popUp() 中 await 后再 prepareI18n(), 保证 platformContractName 已就绪
|
||||
// (与 i18n-lazy mixin 的"boot 完成才能 prepareI18n"契约同源)
|
||||
this.platformContractNameReady = new Promise((resolve) => {
|
||||
this._resolvePlatformContractName = resolve
|
||||
})
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.getPrivacySetting({
|
||||
success: (res) => {
|
||||
this.platformContractName = res.privacyContractName || ''
|
||||
this._resolvePlatformContractName && this._resolvePlatformContractName()
|
||||
},
|
||||
fail: () => {
|
||||
this._resolvePlatformContractName && this._resolvePlatformContractName()
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
@@ -85,13 +114,28 @@ export default {
|
||||
if (res.privacyContractName) {
|
||||
this.platformContractName = res.privacyContractName
|
||||
}
|
||||
this._resolvePlatformContractName && this._resolvePlatformContractName()
|
||||
},
|
||||
fail: () => {
|
||||
this._resolvePlatformContractName && this._resolvePlatformContractName()
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// 无 API 时立即 resolve (避免 await 永久阻塞)
|
||||
this._resolvePlatformContractName && this._resolvePlatformContractName()
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-HARMONY
|
||||
// 鸿蒙元服务: 隐私授权由系统管理, 无需应用层查询 privacyContractName
|
||||
// 鸿蒙元服务: 已接入平台隐私托管, 隐私协议名称由鸿蒙系统管理
|
||||
// 应用层不再调 uni.getPrivacySetting (避免和系统托管弹窗冲突)
|
||||
// 立即 resolve, 让 popUp 的 await 不永久阻塞
|
||||
this._resolvePlatformContractName && this._resolvePlatformContractName()
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// H5 无平台隐私 API, 立即 resolve
|
||||
this._resolvePlatformContractName && this._resolvePlatformContractName()
|
||||
// #endif
|
||||
|
||||
// 提前注册 listener (晚于 created 早于 mounted, 减少事件丢失窗口)
|
||||
@@ -179,14 +223,14 @@ export default {
|
||||
* 统一弹窗 + 关闭其他页面的弹窗
|
||||
* 通过 uni.$emit('privacy:forceClose') 通知其他实例关闭
|
||||
*/
|
||||
showPrivacyPopup() {
|
||||
async showPrivacyPopup() {
|
||||
// 先通知其他实例关闭
|
||||
try { uni.$emit('privacy:forceClose'); } catch (e) {}
|
||||
try { uni.$emit('privacy:forceClose'); } catch (e) { }
|
||||
// 直接弹窗: boot 阶段已经保证 bundle 完整
|
||||
// 完整性校验在 lang.js _bootImpl() 中完成(检查 defaultAppNameMp 等关键 key)
|
||||
// 不完整时 force 重试, 仍不完整则报错不静默回退
|
||||
// 所以这里无需再检测/自愈, 信任 boot 已就绪
|
||||
this.popUp()
|
||||
await this.popUp()
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -265,7 +309,8 @@ export default {
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-HARMONY
|
||||
// 鸿蒙元服务: 隐私授权由系统自动管理, 无需应用层监听
|
||||
// 鸿蒙元服务: 已接入平台隐私托管, 系统级弹窗由鸿蒙自动弹出
|
||||
// 不再监听 uni.onPrivacyAuthorizationChange (避免和系统托管冲突)
|
||||
// #endif
|
||||
},
|
||||
|
||||
@@ -284,22 +329,22 @@ export default {
|
||||
*/
|
||||
closeOtherPagePopUp(closePopUp) {
|
||||
// 通过全局事件通知所有页面关闭弹窗
|
||||
try { uni.$emit('privacy:closeOthers', this._uid); } catch (e) {}
|
||||
try { uni.$emit('privacy:closeOthers', this._uid); } catch (e) { }
|
||||
},
|
||||
|
||||
openPrivacyContract() {
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.openPrivacyContract({
|
||||
success(res) {},
|
||||
fail(err) {}
|
||||
success(res) { },
|
||||
fail(err) { }
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef QUICKAPP-WEBVIEW
|
||||
if (typeof has != 'undefined' && has?.openPrivacySetting) {
|
||||
has.openPrivacySetting({
|
||||
success: (res) => {},
|
||||
fail: (err) => {}
|
||||
success: (res) => { },
|
||||
fail: (err) => { }
|
||||
});
|
||||
} else {
|
||||
this.$util.redirectTo(privacyContractPage);
|
||||
@@ -307,8 +352,8 @@ export default {
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-HARMONY
|
||||
// 鸿蒙元服务: 使用应用内协议页面
|
||||
this.$util.redirectTo(privacyContractPage);
|
||||
// 鸿蒙元服务: 走平台隐私托管, 协议查看由鸿蒙系统接管
|
||||
// (隐私弹窗本身不再在鸿蒙上弹出, 这里也基本不会被触发)
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
@@ -320,8 +365,8 @@ export default {
|
||||
// #ifdef QUICKAPP-WEBVIEW
|
||||
if (typeof has != 'undefined' && has?.openPrivacySetting) {
|
||||
has.openPrivacySetting({
|
||||
success: (res) => {},
|
||||
fail: (err) => {}
|
||||
success: (res) => { },
|
||||
fail: (err) => { }
|
||||
});
|
||||
} else {
|
||||
this.$util.redirectTo(privacyServicePage);
|
||||
@@ -329,7 +374,7 @@ export default {
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-HARMONY
|
||||
this.$util.redirectTo(privacyServicePage);
|
||||
// 鸿蒙元服务: 走平台隐私托管, 协议查看由鸿蒙系统接管
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
@@ -344,7 +389,7 @@ export default {
|
||||
this.privacyResolves.clear()
|
||||
|
||||
// 先通知其他实例关闭, 再关闭自己, 避免叠层
|
||||
try { uni.$emit('privacy:forceClose'); } catch (e) {}
|
||||
try { uni.$emit('privacy:forceClose'); } catch (e) { }
|
||||
this.disPopUp()
|
||||
|
||||
// 修复: 拒绝时不写 privacyAgreed, 允许下次再弹
|
||||
@@ -369,7 +414,7 @@ export default {
|
||||
this.privacyResolves.clear()
|
||||
|
||||
// 先通知其他实例关闭, 再关闭自己
|
||||
try { uni.$emit('privacy:forceClose'); } catch (e) {}
|
||||
try { uni.$emit('privacy:forceClose'); } catch (e) { }
|
||||
this.disPopUp()
|
||||
|
||||
// 由 PrivacyManager 管理 storage
|
||||
@@ -390,9 +435,9 @@ export default {
|
||||
*/
|
||||
i18nPrepareFn() {
|
||||
let appName, privacyContractName, privacyServiceName;
|
||||
|
||||
|
||||
// 关键: appName 必须先解析为字符串, 才能作为 descPrefix/descSuffix 的参数
|
||||
// #ifdef H5 || MP-HARMONY
|
||||
// #ifdef H5
|
||||
appName = this.$lang('components.privacyPopup.defaultAppNameH5');
|
||||
privacyContractName = this.platformContractName || this.$lang('components.privacyPopup.defaultContractNameH5');
|
||||
privacyServiceName = this.$lang('components.privacyPopup.defaultServiceNameH5');
|
||||
@@ -416,20 +461,33 @@ export default {
|
||||
return result;
|
||||
},
|
||||
|
||||
popUp() {
|
||||
async popUp() {
|
||||
// 弹窗前**一次性**计算所有 i18n 文案
|
||||
// boot 阶段已用 isBundleComplete() 保证 bundle 完整
|
||||
// $lang() 返回真实翻译(效果与 H5 一致)
|
||||
// 必须先等两类数据准备好:
|
||||
// 1) Lang.boot() 完成 — 由 i18n-lazy mixin 隐含保证
|
||||
// (调用方 App.vue 在 await PrivacyManager.show() 后才触发弹窗,
|
||||
// PrivacyManager.show() await 了 boot 守卫)
|
||||
// 2) platformContractName 就绪 — 微信/快应用/鸿蒙三端的 API 都是异步的,
|
||||
// success 回调时机不确定, 直接 prepareI18n() 会读到空字符串, 导致
|
||||
// "空串被永久缓存" (与 i18n-lazy mixin 警告的问题同源)
|
||||
// 因此这里 await platformContractNameReady, 然后再 prepareI18n。
|
||||
if (this.platformContractNameReady) {
|
||||
try {
|
||||
await this.platformContractNameReady
|
||||
} catch (e) {
|
||||
// 防御性: 即便 ready 出错也继续 (走 fallback 默认文案)
|
||||
}
|
||||
}
|
||||
this.prepareI18n();
|
||||
this.showPop = true;
|
||||
// 通知 PrivacyManager 弹窗实际显示状态 (用于 rules-updated 双阶段重评估)
|
||||
try { uni.$emit && uni.$emit('privacy:popup-state', { showing: true }) } catch (e) {}
|
||||
try { uni.$emit && uni.$emit('privacy:popup-state', { showing: true }) } catch (e) { }
|
||||
},
|
||||
|
||||
disPopUp() {
|
||||
this.showPop = false;
|
||||
// 通知 PrivacyManager 弹窗实际显示状态 (用于 rules-updated 双阶段重评估)
|
||||
try { uni.$emit && uni.$emit('privacy:popup-state', { showing: false }) } catch (e) {}
|
||||
try { uni.$emit && uni.$emit('privacy:popup-state', { showing: false }) } catch (e) { }
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
@@ -438,6 +496,11 @@ export default {
|
||||
uni.$off('privacy:show', this.handlePrivacyShow)
|
||||
uni.$off('privacy:forceClose', this.handleForceClose)
|
||||
uni.$off('privacy:re-evaluate', this.handleReEvaluate)
|
||||
// 销毁时强制 resolve, 避免 popUp() 中的 await 永久挂起 (例如用户提前离开页面)
|
||||
if (typeof this._resolvePlatformContractName === 'function') {
|
||||
this._resolvePlatformContractName()
|
||||
this._resolvePlatformContractName = null
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -491,7 +554,7 @@ export default {
|
||||
.privacy-button-btn {
|
||||
color: #FFF;
|
||||
font-size: 30rpx;
|
||||
/* #ifdef QUICKAPP-WEBVIEW || H5 || MP-HARMONY */
|
||||
/* #ifdef QUICKAPP-WEBVIEW || H5 */
|
||||
font-size: 28rpx;
|
||||
/* #endif */
|
||||
font-weight: 500;
|
||||
|
||||
Reference in New Issue
Block a user