refactor(privacy-popup): 抽取通用延迟i18n混入并重构隐私弹窗

1. 新建通用的i18n延迟初始化混入,解决小程序/鸿蒙环境下i18n空串缓存问题
2. 将隐私弹窗原有的prepareI18n逻辑迁移到混入中,统一实现i18nPrepareFn
3. 简化弹窗组件内的i18n处理代码,优化可维护性
This commit is contained in:
2026-07-01 14:04:42 +08:00
parent 24ae55867a
commit c0220492a2
2 changed files with 109 additions and 57 deletions

View File

@@ -31,12 +31,14 @@
import has from '@system.has'
// #endif
import PrivacyManager from '@/common/js/privacy.js';
import i18nLazyMixin from '@/common/mixin/i18n-lazy.js';
const privacyContractPage = '/pages_tool/agreement/contenr?type=0';
const privacyServicePage = '/pages_tool/agreement/contenr?type=1';
export default {
name: 'PrivacyPopup',
mixins: [i18nLazyMixin],
data() {
return {
agree: false,
@@ -44,12 +46,12 @@ export default {
privacyAuthorization: null,
privacyResolves: new Set(),
// 平台提供的隐私协议名称(由 wx.getPrivacySetting 等异步设置)
// 弹窗前才参与计算 i18n.privacyContractName
// 弹窗前才参与 i18n.privacyContractName 的填充
platformContractName: '',
// 弹窗文案: 在 popUp() 之前由 prepareI18n() 一次性计算
// 关键: 不在 data() 中调 $lang(避免 boot 未完成时缓存空串)
// 也不在 mounted/computed 中调(method/computed 响应式不稳定)
// 此时 Lang.boot() 必然已完成(PrivacyManager.show() 在 await boot() 后才执行)
// 弹窗文案: 模板只读 i18n.xxx
// 初值为空串, 弹窗前在 popUp() 中**同步**填充
// 此时 App.vue 已 await boot() + await PrivacyManager.show() 完成,
// Lang.boot() 必然已完成, $lang() 返回真实翻译
i18n: {
title: '',
descPrefix: '',
@@ -110,57 +112,6 @@ export default {
}
},
methods: {
/**
* 在弹窗前**一次性**计算所有 i18n 文案
* 关键修复: 微信小程序首次启动时, main.js 同步 $mount() 导致 data() 求值时
* Lang.boot() 尚未完成, $lang() 返回空串; 即使 boot 完成后 emit $forceUpdate,
* data 字段不会重新求值, computed 依赖追踪也可能被压缩器优化掉。
* 此方法在 popUp() 之前同步执行, 此时 Lang.boot() 必然已完成,
* 一次性把 i18n 字段填好, 模板只读 data 即可, 效果与 H5 一致。
*/
prepareI18n() {
const $lang = this.$lang.bind(this);
// 应用名称(按平台取值)
// #ifdef H5 || MP-HARMONY
const appName = $lang('components.privacyPopup.defaultAppNameH5');
// #endif
// #ifndef H5 || MP-HARMONY
const appName = $lang('components.privacyPopup.defaultAppNameMp');
// #endif
// 隐私协议名称: 平台提供的优先, 否则回退 i18n 默认值
let privacyContractName;
if (this.platformContractName) {
privacyContractName = this.platformContractName;
} else {
// #ifdef H5 || MP-HARMONY
privacyContractName = $lang('components.privacyPopup.defaultContractNameH5');
// #endif
// #ifndef H5 || MP-HARMONY
privacyContractName = $lang('components.privacyPopup.defaultContractNameMp');
// #endif
}
// 服务协议名称(仅 H5/鸿蒙端显示)
// #ifdef H5 || MP-HARMONY
const privacyServiceName = $lang('components.privacyPopup.defaultServiceNameH5');
// #endif
// #ifndef H5 || MP-HARMONY
const privacyServiceName = '';
// #endif
this.i18n = {
title: $lang('components.privacyPopup.title'),
descPrefix: $lang('components.privacyPopup.descPrefix', { appName }),
descSuffix: $lang('components.privacyPopup.descSuffix', { appName }),
privacyContractName,
privacyServiceName,
disagreeBtn: $lang('components.privacyPopup.disagreeBtn'),
agreeBtn: $lang('components.privacyPopup.agreeBtn'),
disagreeTip: $lang('components.privacyPopup.disagreeTip'),
};
},
/**
* 接收全局隐私弹窗事件
* 由 App.vue → PrivacyManager.show() → uni.$emit('privacy:show') 触发
@@ -380,11 +331,41 @@ export default {
this.$emit('agree')
},
/**
* mixin i18n-lazy 要求的方法
* 返回一个对象, key 与 data().i18n 字段一一对应
* 调用时机在 popUp() 中(此时 boot 已完成, $lang() 返回真实翻译)
*/
i18nPrepareFn() {
// 关键: appName 必须先解析为字符串, 才能作为 descPrefix/descSuffix 的参数
// #ifdef H5 || MP-HARMONY
const appName = this.$lang('components.privacyPopup.defaultAppNameH5');
const privacyContractName = this.platformContractName || this.$lang('components.privacyPopup.defaultContractNameH5');
const privacyServiceName = this.$lang('components.privacyPopup.defaultServiceNameH5');
// #endif
// #ifdef MP-WEIXIN
const appName = this.$lang('components.privacyPopup.defaultAppNameMp');
const privacyContractName = this.platformContractName || this.$lang('components.privacyPopup.defaultContractNameMp');
const privacyServiceName = '';
// #endif
return {
title: this.$lang('components.privacyPopup.title'),
descPrefix: this.$lang('components.privacyPopup.descPrefix', { appName }),
descSuffix: this.$lang('components.privacyPopup.descSuffix', { appName }),
privacyContractName,
privacyServiceName,
disagreeBtn: this.$lang('components.privacyPopup.disagreeBtn'),
agreeBtn: this.$lang('components.privacyPopup.agreeBtn'),
disagreeTip: this.$lang('components.privacyPopup.disagreeTip'),
};
},
popUp() {
// 弹窗前**一次性**计算所有 i18n 文案
// 此时 App.vue 已 await boot() + await PrivacyManager.show() 完成,
// Lang.boot() 必然已完成, $lang() 返回真实翻译(效果与 H5 一致)
this.prepareI18n()
this.prepareI18n();
this.showPop = true;
},