fix(h5): 修复 WebView 顶栏 logo 与内容重叠
embed 模式 header 改文档流、移除负 margin;持久化 sbh 并在路由中保留,避免胶囊区与档案条重叠。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user