fix(hover-nav): 修复H5端语言切换时导航跟随页面位移的问题

1.  将H5端的hover-nav组件通过Teleport方式挂载到document.body,避开页面transform的影响
2.  同步store中的主题色变量到组件根元素样式,修复样式继承失效问题
3.  在组件销毁前将DOM元素移回原父节点避免内存泄漏
4.  简化当前语言显示逻辑,统一展示当前选中语言的短码
This commit is contained in:
2026-07-02 14:02:52 +08:00
parent de52406be8
commit c430c8971c

View File

@@ -221,32 +221,12 @@ export default {
const info = this.$store && this.$store.state ? (this.$store.state.shopInfo || this.shopInfo) : this.shopInfo;
return !!(info && info.ischina) && Array.isArray(this.langList) && this.langList.length >= 2;
},
// 按钮上显示的文案:
// - ≥3 种语言(选择器模式):展示"当前选中语言"的短码(选择器里有 ✓ 标识)
// - =2 种语言(原循环模式):展示"下一个要切到的语言"的短码(原 hover-nav 行为)
// 按钮上显示的文案:始终展示当前选中语言的短码
// 短码来源:initLanguage 已用合并后的 langDisplayMap 填充到 langList[i].name,
// 优先用后端 langDisplayNames,否则用前端 LANG_DISPLAY_NAMES
currentLangDisplayName() {
const list = this.langList;
if (!Array.isArray(list) || list.length === 0) return '';
const len = list.length;
// ≥3 种语言:展示当前
if (len >= 3) {
const currentItem = list[this.currentLangIndex];
if (!currentItem) return '';
return currentItem.name || getLangShortCode(currentItem.value, this.langDisplayMap);
}
// =2 种语言:展示下一个(原逻辑)
if (len === 2) {
const nextIndex = (this.currentLangIndex + 1) % len;
const nextItem = list[nextIndex];
if (!nextItem) return '';
return nextItem.name || getLangShortCode(nextItem.value, this.langDisplayMap);
}
// =1 种语言(理论不会显示按钮,但兜底):展示当前
const currentItem = list[this.currentLangIndex];
if (!currentItem) return '';
return currentItem.name || getLangShortCode(currentItem.value, this.langDisplayMap);
@@ -462,6 +442,56 @@ export default {
// #ifdef H5
this.readLayoutVars();
// #endif
// ============================================================
// H5 下把 hover-nav 从页面 DOM 树搬到 document.body(Teleport)
// ------------------------------------------------------------
// 问题: 切换语言时 $langConfig.change() 内部调用 uni.reLaunch(),
// H5 的 reLaunch 会触发页面过渡动画(给页面容器加 transform)。
// CSS 规范: position: fixed 在 transform 父级下会失效,
// 变成相对于被 transform 的父级定位,导致 hover-nav 跟着
// 页面过渡一起"上移-下移"。
//
// 解决: 在 mounted() 中把 this.$el 搬到 document.body 下。
// 这样 hover-nav 不再是页面组件的子元素,页面过渡的 transform
// 完全不影响它。position: fixed 相对于 body(viewport) 正常工作。
//
// 副作用: 搬家后,页面根元素上的 CSS 变量(themeColor,如
// --hover-nav-bg-color)不再通过继承传递给 hover-nav,
// 因为 body 不在页面的 DOM 子树里。
// 修复: 把 $store.state.themeColor 同步到 this.$el 的 inline style,
// 让 hover-nav 自己持有这些 CSS 变量,不再依赖页面继承。
//
// 注意: this.$el 是 Vue 组件的根元素(模板最外层的 <view>),
// 搬走它后 Vue 的响应式、事件、生命周期**完全不受影响**,
// 因为 Vue 的虚拟 DOM 与真实 DOM 位置解耦。
// 这是 Vue 2 的合法模式,等同于 Vue 3 的 <Teleport to="body">。
// ============================================================
// #ifdef H5
if (typeof document !== 'undefined' && document.body && this.$el) {
this._origParent = this.$el.parentNode;
document.body.appendChild(this.$el);
// 搬家后,把页面根元素上的 CSS 变量同步到 hover-nav 自己身上
// 主题系统把 themeColor 挂到页面根元素(如 index.vue 的 <view :style="themeColor">),
// 搬家后 hover-nav 不在页面子树里,继承不到这些变量。
// 这里直接从 store 读 themeColor 写到 this.$el 的 style 上,
// 让 .btn-item .common-bg 能正常读到 --hover-nav-bg-color 等变量。
const tc = this.$store && this.$store.state && this.$store.state.themeColor;
if (tc && this.$el.style) {
this.$el.style.cssText = (this.$el.style.cssText || '') + ';' + tc;
}
}
// #endif
},
beforeDestroy() {
// H5 Teleport 还原:把元素从 body 移回原父节点,避免内存泄漏
// #ifdef H5
if (this._origParent && this.$el && this.$el.parentNode === document.body) {
this._origParent.appendChild(this.$el);
this._origParent = null;
}
// #endif
},
methods: {
// #ifdef H5