refactor(鸿蒙元服务): 适配鸿蒙平台隐私托管与启动屏时序优化
1. 适配鸿蒙元服务平台隐私托管,由系统自动弹出隐私弹窗,应用层不再弹出自建弹窗 2. 新增启动屏strict模式,鸿蒙场景下严格等待首页DIY渲染完成再关闭启动屏,避免白屏 3. 新增splash-state单元测试覆盖新旧模式与鸿蒙场景 4. 清理隐私相关冗余代码与注释,统一平台逻辑分支
This commit is contained in:
@@ -147,20 +147,13 @@ function getQuickAppPrivacySetting() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 鸿蒙元服务: 查询平台级隐私设置
|
||||
* 鸿蒙元服务的隐私授权由系统自动管理,应用层只需检查 storage
|
||||
* @returns {Promise<{needAuthorization: boolean, privacyContractName?: string}>}
|
||||
* 鸿蒙元服务: 已接入平台隐私托管, 应用层不再调任何系统 API
|
||||
* 仅为兼容历史代码保留此函数 (被 checkPrivacy() 调用), 内部只走 storage 路径
|
||||
* @returns {Promise<{needAuthorization: boolean}>}
|
||||
*/
|
||||
function getHarmonyPrivacySetting() {
|
||||
return new Promise((resolve) => {
|
||||
// #ifdef MP-HARMONY
|
||||
// 鸿蒙元服务: 隐私授权由系统管理,应用层仅检查 storage
|
||||
// 系统会在用户首次使用隐私API时自动弹出系统级隐私授权弹窗
|
||||
resolve({ needAuthorization: !getStorageAgreed() });
|
||||
// #endif
|
||||
// #ifndef MP-HARMONY
|
||||
resolve({ needAuthorization: !getStorageAgreed() });
|
||||
// #endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -188,7 +181,7 @@ async function checkPrivacy() {
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 鸿蒙元服务: 隐私由系统管理,仅查 storage
|
||||
// 鸿蒙元服务: 调系统 getPrivacySetting (无 API 时 getHarmonyPrivacySetting 内部回退 storage)
|
||||
// H5 / 其他平台: 仅查 storage
|
||||
return checkPrivacySync();
|
||||
}
|
||||
@@ -730,8 +723,9 @@ const PrivacyManager = {
|
||||
|
||||
// 优化: storage 已同意时直接走同步快速路径, 不调 async checkPrivacy()
|
||||
// 节省 1 个 microtask (~0-4ms, dev 模式更明显)
|
||||
// 注意: 微信小程序/快应用/鸿蒙走 checkPrivacy (平台 API 异步)
|
||||
// 注意: 微信小程序/快应用走 checkPrivacy (平台 API 异步)
|
||||
// H5 下 checkPrivacy 内部已 sync 化 (修过), 直接 await 即可
|
||||
// 鸿蒙元服务: 不调任何系统 API, 不弹自建弹窗 (走平台托管)
|
||||
const result = _storageAgreed ? checkPrivacySync() : await checkPrivacy();
|
||||
|
||||
_needShow = result.needAuthorization;
|
||||
@@ -741,7 +735,18 @@ const PrivacyManager = {
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置待弹窗标志
|
||||
// #ifdef MP-HARMONY
|
||||
// 鸿蒙元服务: 已接入平台隐私托管, 由鸿蒙系统自动弹出系统级隐私弹窗
|
||||
// 应用层不再 emit 'privacy:show' 触发自建 privacy-popup.vue
|
||||
// 标记已决策, 防止 consumePendingShow 兜底分支在 page mounted 时再次弹自建弹窗
|
||||
_needShow = false;
|
||||
_pendingShow = false;
|
||||
_urlRuleDecided = true;
|
||||
console.log('[privacy] MP-HARMONY: 已委托鸿蒙平台隐私托管, 不触发自建弹窗');
|
||||
return;
|
||||
// #endif
|
||||
|
||||
// 其他平台: 设置待弹窗标志 + emit 触发自建弹窗
|
||||
// 已 mounted 的页面组件通过事件接收
|
||||
// 未 mounted 的页面组件 mounted 时通过 consumePendingShow 补偿
|
||||
_pendingShow = true;
|
||||
@@ -757,18 +762,28 @@ const PrivacyManager = {
|
||||
* 页面 mounted 时补偿检查: 若 show() 在页面 mounted 之前 emit 了事件
|
||||
* 此方法返回 true 并清除标志,页面组件据此自动弹窗
|
||||
*
|
||||
* 鸿蒙元服务兼容:
|
||||
* 历史兼容:
|
||||
* Lang.boot() 可能 hang, PrivacyManager.show() 可能从未被调用过;
|
||||
* 此时 _pendingShow 仍是默认 false, 但用户从未同意过隐私, 必须强制弹一次。
|
||||
* 所以增加 storage 检测: 用户未同意也返回 true (补偿弹窗)。
|
||||
*
|
||||
* 鸿蒙元服务(双轨制):
|
||||
* 已接入平台隐私托管, 由鸿蒙系统自动弹出系统级隐私弹窗,
|
||||
* 应用层不再 emit/弹自建弹窗, 兜底分支也必须返回 false, 否则会与
|
||||
* 鸿蒙系统级弹窗叠加出现"多次弹出"。
|
||||
* @returns {boolean} true=需要弹窗,false=不需要
|
||||
*/
|
||||
consumePendingShow() {
|
||||
// #ifdef MP-HARMONY
|
||||
// 鸿蒙元服务: 已接入平台隐私托管, 不弹自建弹窗
|
||||
return false;
|
||||
// #endif
|
||||
|
||||
if (_pendingShow && _needShow) {
|
||||
_pendingShow = false;
|
||||
return true;
|
||||
}
|
||||
// 鸿蒙兜底: PrivacyManager.show() 没跑过 + 用户没同意过 storage → 必须弹
|
||||
// 兜底: PrivacyManager.show() 没跑过 + 用户没同意过 storage → 必须弹
|
||||
// 同一次进程内只补一次,避免隐私弹窗被反复触发
|
||||
// 关键: URL/路由规则已经决策过 "本次跳过" 时, 不应触发兜底弹窗 (白名单场景)
|
||||
if (!_hasChecked && !_pendingShow && !_urlRuleDecided && !getStorageAgreed()) {
|
||||
|
||||
@@ -136,15 +136,22 @@ class SplashState {
|
||||
* @param {string} [opts.mode] - 离开动画模式
|
||||
* - 'default': 默认 480ms 动画
|
||||
* - 'fast': 80ms 短动画, 用于隐私弹窗等需要立即接管视觉的场景, 避免"双遮罩"感
|
||||
* - 'strict': 严格模式, 不走 MOUNT_GRACE_MS / PAGE_READY_GRACE_MS 兜底,
|
||||
* 只等 notifyPageReady() 或 DEFAULT_TIMEOUT 总超时
|
||||
* 适用于: 已接入平台托管的系统弹窗 (鸿蒙),
|
||||
* splash 必须真等到首页 diy 内容渲染好才能关
|
||||
*/
|
||||
hide(opts = {}) {
|
||||
const reason = opts.reason || 'manual';
|
||||
const useStrict = opts.mode === 'strict' || opts._strict === true;
|
||||
// boot-complete 默认走软隐藏, 避免时序问题
|
||||
const useSoft = opts.soft === true || (opts.soft !== false && reason === 'boot-complete');
|
||||
const mode = opts.mode === 'fast' ? 'fast' : 'default';
|
||||
const useSoft = useStrict || opts.soft === true || (opts.soft !== false && reason === 'boot-complete');
|
||||
// strict 模式复用 fast 动画
|
||||
const mode = useStrict ? 'fast' : (opts.mode === 'fast' ? 'fast' : 'default');
|
||||
|
||||
if (useSoft) {
|
||||
this._softHide({ reason, mode });
|
||||
// 传递完整 opts, _softHide 内部能识别 mode='strict' 或 _strict
|
||||
this._softHide(opts);
|
||||
} else {
|
||||
this._doHide({ reason, mode });
|
||||
}
|
||||
@@ -154,10 +161,17 @@ class SplashState {
|
||||
* 软隐藏: 标记应该 hide, 等组件 mount + 页面就绪
|
||||
* 如果组件 1s 内没 mount, 兜底自动 hide
|
||||
* 如果页面 2s 内没就绪, 兜底自动 hide
|
||||
*
|
||||
* strict 模式 (鸿蒙已接入平台托管):
|
||||
* 不走 MOUNT_GRACE_MS / PAGE_READY_GRACE_MS 兜底
|
||||
* 只等 notifyMounted() + notifyPageReady() 或 DEFAULT_TIMEOUT 总超时
|
||||
* 保证 splash 一定等到首页 diy 内容真的渲染好
|
||||
*/
|
||||
_softHide(opts) {
|
||||
if (!this._visible) return; // 已经隐藏了
|
||||
this._pendingHide = opts;
|
||||
// strict 模式识别: mode='strict' 或 _strict=true
|
||||
const useStrict = opts && (opts.mode === 'strict' || opts._strict === true);
|
||||
|
||||
// 如果组件已经 mount 且页面已就绪, 立即隐藏
|
||||
if (this._hasMounted && this._pageReady) {
|
||||
@@ -165,6 +179,12 @@ class SplashState {
|
||||
return;
|
||||
}
|
||||
|
||||
// strict 模式: 不设 mountGraceId 兜底, 只等 notifyPageReady
|
||||
// (DEFAULT_TIMEOUT 在 show() 设置的 _timeoutId 兜底)
|
||||
if (useStrict) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 兜底: 1s 内没 mount, 强制隐藏
|
||||
if (this._mountGraceId) clearTimeout(this._mountGraceId);
|
||||
this._mountGraceId = setTimeout(() => {
|
||||
@@ -177,15 +197,23 @@ class SplashState {
|
||||
/**
|
||||
* 真正触发软隐藏
|
||||
* 如果页面还未就绪, 等待页面就绪后再 hide
|
||||
* strict 模式 (鸿蒙): 不设 pageReadyGraceId 兜底, 只等 notifyPageReady()
|
||||
*/
|
||||
_flushPendingHide() {
|
||||
if (this._mountGraceId) {
|
||||
clearTimeout(this._mountGraceId);
|
||||
this._mountGraceId = null;
|
||||
}
|
||||
const _opts = this._pendingHide || {}
|
||||
const useStrict = _opts && (_opts.mode === 'strict' || _opts._strict === true)
|
||||
|
||||
// 如果页面还未就绪, 等待页面就绪 (保留 _pendingHide, 等页面就绪后再处理)
|
||||
if (!this._pageReady) {
|
||||
// strict 模式: 不设 pageReadyGraceId, 只等 notifyPageReady()
|
||||
// 总超时由 show() 里的 _timeoutId 兜底 (DEFAULT_TIMEOUT=8s)
|
||||
if (useStrict) {
|
||||
return;
|
||||
}
|
||||
// 兜底: 2s 内没就绪, 强制隐藏
|
||||
if (this._pageReadyGraceId) clearTimeout(this._pageReadyGraceId);
|
||||
this._pageReadyGraceId = setTimeout(() => {
|
||||
|
||||
Reference in New Issue
Block a user