fix(h5): 修复 WebView 顶栏 logo 与内容重叠

embed 模式 header 改文档流、移除负 margin;持久化 sbh 并在路由中保留,避免胶囊区与档案条重叠。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-25 01:22:39 +08:00
co-authored by Cursor
parent 9d4fb52d29
commit f4bd57ce1b
7 changed files with 48 additions and 14 deletions
+8 -1
View File
@@ -1,5 +1,5 @@
<template>
<header class="app-header">
<header class="app-header" :class="{ 'is-embed': mpEmbed }">
<div class="side left">
<BackButton v-if="showBack" />
</div>
@@ -15,8 +15,10 @@ import { computed } from 'vue'
import { useRoute } from 'vue-router'
import BackButton from './BackButton.vue'
import BrandLogo from './BrandLogo.vue'
import { isMpEmbed } from '../lib/mpEmbed'
const route = useRoute()
const mpEmbed = isMpEmbed()
/** 首页 / 我的 为 Tab 根页不显示返回;探索可从首页进入,显示返回 */
const showBack = computed(() => {
@@ -40,6 +42,11 @@ const showBack = computed(() => {
background: transparent;
pointer-events: none;
}
.app-header.is-embed {
position: relative;
padding: calc(6px + var(--yxg-safe-top)) 12px 8px;
background: transparent;
}
.side {
display: flex;
align-items: center;
+1 -3
View File
@@ -35,16 +35,14 @@ withDefaults(
.ps {
position: relative;
overflow-x: hidden;
margin-top: calc(-1 * var(--yxg-safe-top));
padding-top: var(--yxg-page-top);
padding-bottom: var(--spacing-xs);
min-height: calc(100% + var(--yxg-safe-top));
min-height: 100%;
background: var(--yxg-page-wash);
}
.ps-flat {
background: transparent;
min-height: 0;
margin-top: calc(-1 * var(--yxg-safe-top));
padding-top: var(--yxg-page-top);
}
.atm {
+6 -2
View File
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { initMpEmbed, isMpEmbed } from './mpEmbed'
import { applyMpEmbedDom, initMpEmbed, isMpEmbed } from './mpEmbed'
describe('mpEmbed', () => {
beforeEach(() => {
@@ -17,11 +17,15 @@ describe('mpEmbed', () => {
expect(isMpEmbed()).toBe(true)
})
it('applies status bar height from ?sbh=', () => {
it('persists status bar height from ?sbh=', () => {
window.history.replaceState({}, '', '/psy/?mp=1&sbh=47')
initMpEmbed()
expect(document.documentElement.style.getPropertyValue('--yxg-safe-top')).toBe('47px')
expect(document.documentElement.style.getPropertyValue('--yxg-page-top')).toBe('calc(47px + 50px)')
expect(sessionStorage.getItem('yxg_sbh')).toBe('47')
window.history.replaceState({}, '', '/psy/?mp=1')
applyMpEmbedDom()
expect(document.documentElement.style.getPropertyValue('--yxg-safe-top')).toBe('47px')
})
it('returns false without mp query', () => {
+11
View File
@@ -1,6 +1,7 @@
const MP_EMBED_KEY = 'yxg_mp_embed'
const MP_QUERY = 'mp'
const SBH_QUERY = 'sbh'
const SBH_KEY = 'yxg_sbh'
const HEADER_H_PX = 50
function applySafeTopInset(px: number): void {
@@ -11,11 +12,20 @@ function applySafeTopInset(px: number): void {
root.style.setProperty('--yxg-page-top', `calc(${safe} + ${HEADER_H_PX}px)`)
}
function readStoredSbh(): number {
if (typeof sessionStorage === 'undefined') return 0
const raw = sessionStorage.getItem(SBH_KEY)
if (!raw || !/^\d+$/.test(raw)) return 0
return parseInt(raw, 10)
}
/** Apply document-level embed chrome (safe-area wash, hide HTML title). */
export function applyMpEmbedDom(): void {
if (typeof document === 'undefined' || !isMpEmbed()) return
document.title = '\u200b'
document.documentElement.classList.add('mp-embed')
const stored = readStoredSbh()
if (stored > 0) applySafeTopInset(stored)
}
/** Persist ?mp=1 from mini-program web-view entry. Call once before router mount. */
@@ -27,6 +37,7 @@ export function initMpEmbed(): void {
}
const sbh = params.get(SBH_QUERY)
if (sbh && /^\d+$/.test(sbh)) {
sessionStorage.setItem(SBH_KEY, sbh)
applySafeTopInset(parseInt(sbh, 10))
}
applyMpEmbedDom()
+1 -2
View File
@@ -48,10 +48,9 @@ const {
.home {
position: relative;
overflow-x: hidden;
margin-top: calc(-1 * var(--yxg-safe-top));
padding-top: var(--yxg-page-top);
padding-bottom: var(--spacing-xs);
min-height: calc(100% + var(--yxg-safe-top));
min-height: 100%;
background: var(--yxg-page-wash);
}
.atm {
+14 -2
View File
@@ -50,8 +50,20 @@ const router = createRouter({
})
router.beforeEach((to) => {
if (!isMpEmbed() || to.query.mp === '1') return true
return { path: to.path, query: { ...to.query, mp: '1' }, hash: to.hash, replace: true }
if (!isMpEmbed()) return true
const q = { ...to.query }
let changed = false
if (q.mp !== '1') {
q.mp = '1'
changed = true
}
const storedSbh = sessionStorage.getItem('yxg_sbh')
if (storedSbh && q.sbh !== storedSbh) {
q.sbh = storedSbh
changed = true
}
if (!changed) return true
return { path: to.path, query: q, hash: to.hash, replace: true }
})
export default router
+7 -4
View File
@@ -52,15 +52,18 @@ html.mp-embed body{
.app-shell.mp-embed::before{
content:"";
position:fixed;
top:calc(-1 * var(--yxg-safe-top));
left:0;
right:0;
bottom:0;
inset:0;
z-index:0;
pointer-events:none;
background:var(--yxg-body-wash);
background-color:var(--color-bg-start);
}
.app-shell.mp-embed .home,
.app-shell.mp-embed .ps{
margin-top:0;
padding-top:8px;
min-height:100%;
}
.app-footer{
position:relative;
z-index:1;