修「点头像没反应」:FbPersonSheet 塞进 bindSheet 会被立刻关掉(宿主契约冲突)

真机验证时抓到 —— 三条里第 ① 条第一轮**完全没用**:
点头像什么都不出来。每次点击日志都出现一组:
  AceSheet: radius is not correct type
  AceDrag: Drag gesture has been canceled     ← 浮层起来立刻被取消
⇒ sheet **确实创建了**,但被测量失败关掉;界面上看就是"点了没反应"。
(我第一轮据此误判成 "onClick 没接上",白查了一轮点击链路 —— 点击是好的。)

根因:FbPersonSheet 的根节点是
    Column() { /* 92% 宽的圆角卡 */ }
      .width('100%').height('100%').justifyContent(FlexAlign.Center)
—— "铺满全屏 + 内部居中",因为它的**设计宿主**是
"页面里叠一层遮罩 + Stack 居中"(名册页、日程页都这么用)。
而 bindSheet 要求内容**按自身高度自适应** ⇒
"内容要铺满 100% 高" vs "sheet 要按内容定高" 互相打架 ⇒ 判为无效、立刻关。

修法(最小侵入,老调用点一个字不改):
① 给 FbPersonSheet 加 `@Param embedded: boolean = false`:
     .width(this.embedded ? 'auto' : '100%')
     .height(this.embedded ? 'auto' : '100%')
     .justifyContent(this.embedded ? FlexAlign.Start : FlexAlign.Center)
     .padding(bottom 在 embedded 时去掉"贴底手势区留白"那 16)
   ⇒ 默认 false = 老行为;顶栏那处传 embedded: true。
② sheet 高度用 SheetSize.LARGE 而不是 FIT_CONTENT
   (卡片自带 maxHeight:72%,FIT_CONTENT 下会跟它抢高度)。
③ 删掉外层 myDetailPanel 上的 constraintSize({maxHeight:'82%'})
   —— 高度由 sheet 定,三方都设 = 三方打架。

真机复验(装机后立即点):
  浮层完整弹出且用的是**接口那份完整数据**(含本地没有的字段):
  「杨永全 · 副教授 · 家中排行第 2 个 · 生日 11-16 · 邮箱 i@yangyq.net ·
    地址 青岛市 · 身份证 372****4856 · 其它邮箱 yangyq@ouc.edu.cn ·
    网址 https://yangyq.net · 工作经历 中国海洋大学|副教授」+ [编辑][关闭]
  客观判据:uitest dumpLayout 的布局树字节数 119207 → 162736。

验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 352/0(再增 5 条钉住
embedded / SheetSize.LARGE / 不铺满 / 组件有开关);_fb_v59b_falsify.py 11/0。
另修一条旧断言(L 组"不再占满宽"原来锚死 `width('92%')` 字面量,
放宽为接受条件式,但没退化成"文件里有 92% 就行")。
This commit is contained in:
2026-10-18 21:22:43 +08:00
parent f0f0d9047a
commit 9bf21cd853
2 changed files with 55 additions and 8 deletions
@@ -56,6 +56,27 @@ export struct FbPersonSheet {
*/ */
@Event onEdit: () => void = (): void => { @Event onEdit: () => void = (): void => {
}; };
/**
* 嵌入模式:宿主**自己已经是浮层**(如 `bindSheet`)时为 true。
*
* 🔴🔴 为什么必须有这个开关(2026-10-18 真机踩出来的真 bug):
* 本组件的根节点是 `Column().width('100%').height('100%')`(**铺满全屏**、
* 内部再垂直居中那张 92% 宽的卡)—— 因为它的设计宿主是
* "页面里叠一层遮罩 + Stack 居中"(名册页就是这么用的)。
*
* 而顶栏点头像想复用这个组件时,我把它塞进了 `bindSheet`:
* **sheet 要做"高度自适应内容"(`SheetSize.FIT_CONTENT`),
* 内容却要求"铺满 100% 高度"** ⇒ 测量互相打架。
* 真机症状:**每次点击 `AceSheet` 都创建了,却紧接着
* `AceDrag: Drag gesture has been canceled` —— 浮层起来立刻被关掉**
* (界面看上去"点了没反应",最容易被误判成"点击没接上")。
*
* ⇒ `embedded = true` 时:去掉 `width/height 100%` 与内层居中,
* 只留那张卡(宽度交给 sheet、高度自适应),sheet 自己负责
* 底部手势区与遮罩。
* ⚠️ 默认 `false` = 老行为,**名册/日程那几个调用点一个字都不用改**。
*/
@Param embedded: boolean = false;
/** 一行的图标资源 */ /** 一行的图标资源 */
private iconOf(name: string): Resource { private iconOf(name: string): Resource {
@@ -465,17 +486,29 @@ export struct FbPersonSheet {
// **不要让它左右充满整个屏幕,下面也不要挨着屏幕**, // **不要让它左右充满整个屏幕,下面也不要挨着屏幕**,
// 而是放在**屏幕中间**,采用**圆角浮窗**式的形式。」 // 而是放在**屏幕中间**,采用**圆角浮窗**式的形式。」
// ⇒ 不再贴底满宽:左右留边(92%)、四角圆角、垂直居中(由宿主的 Stack 对齐)。 // ⇒ 不再贴底满宽:左右留边(92%)、四角圆角、垂直居中(由宿主的 Stack 对齐)。
.width('92%') //
// ⚠️ `embedded`(宿主自己就是 sheet)时宽度交给 sheet 决定 ——
// 再用 92% 会在 sheet 里又缩一次,看上去"卡片比容器窄一圈"。
.width(this.embedded ? 'auto' : '92%')
// 最多占屏幕 72%:上方留出可见列表,暗示"这是浮层、下面还有内容" // 最多占屏幕 72%:上方留出可见列表,暗示"这是浮层、下面还有内容"
.constraintSize({ maxHeight: '72%' }) .constraintSize({ maxHeight: this.embedded ? '100%' : '72%' })
.padding({ left: 18, right: 18, top: 18, bottom: 20 + FB_SHEET_BOTTOM_GAP }) // ⚠️ embedded 时**去掉底部手势区那段留白**:那 16 是给
// "贴底遮罩 + 隐藏手势条"的场景补的(见 FB_SHEET_BOTTOM_GAP),
// 而 bindSheet 自己已经把手势区让出来了 ⇒ 再补就是白白多一截空白。
.padding({
left: 18, right: 18, top: 18,
bottom: this.embedded ? 20 : 20 + FB_SHEET_BOTTOM_GAP
})
.backgroundColor(FbColors.surface) .backgroundColor(FbColors.surface)
.borderRadius(FbMetric.radius) .borderRadius(FbMetric.radius)
.alignItems(HorizontalAlign.Start) .alignItems(HorizontalAlign.Start)
} }
.width('100%') /* 🔴 embedded(宿主自己就是 sheet)时**不能再铺满 + 居中**:
.height('100%') 内容是"按自适应高度"交给 sheet 的,铺 100% 高会与
.justifyContent(FlexAlign.Center) `SheetSize.FIT_CONTENT` 互相打架 ⇒ 浮层起来立刻被关(真机实测)。 */
.width(this.embedded ? 'auto' : '100%')
.height(this.embedded ? 'auto' : '100%')
.justifyContent(this.embedded ? FlexAlign.Start : FlexAlign.Center)
.hitTestBehavior(HitTestMode.Transparent) .hitTestBehavior(HitTestMode.Transparent)
} }
} }
+16 -2
View File
@@ -243,7 +243,10 @@ export struct FbTopBar {
// ⚠️ `onDisappear` 必须复位 `myDetailSheet`(点遮罩/下滑关闭都会走它), // ⚠️ `onDisappear` 必须复位 `myDetailSheet`(点遮罩/下滑关闭都会走它),
// 否则下次点头像**打不开**(状态位还是 true 但面板已经不在)。 // 否则下次点头像**打不开**(状态位还是 true 但面板已经不在)。
.bindSheet(this.myDetailSheet, this.myDetailPanel(), { .bindSheet(this.myDetailSheet, this.myDetailPanel(), {
height: SheetSize.FIT_CONTENT, // ⚠️ 用 `LARGE`(= 大高度、内容自适应)而不是 `FIT_CONTENT`:
// 详情卡片有 `maxHeight: '72%'` 的内部约束,FIT_CONTENT 下
// 系统会试图把 sheet 压到"刚好包住内容",与那个百分比约束打架。
height: SheetSize.LARGE,
dragBar: true, dragBar: true,
showClose: false, showClose: false,
backgroundColor: FbColors.surface, backgroundColor: FbColors.surface,
@@ -376,6 +379,14 @@ export struct FbTopBar {
} else if (this.myDetail !== null) { } else if (this.myDetail !== null) {
FbPersonSheet({ FbPersonSheet({
person: this.myDetail, person: this.myDetail,
/* 🔴🔴 必须传 embedded(2026-10-18 真机踩出来的真 bug):
`FbPersonSheet` 的根节点是"铺满全屏 + 内部居中"的浮层形态
(它的设计宿主是"页面里叠遮罩 + Stack 居中",名册页就这么用)。
直接塞进 `bindSheet`(内容要自适应高度)会**测量互相打架**:
真机症状是**每次点击 sheet 都创建了、紧接着
`AceDrag: Drag gesture has been canceled` —— 起来立刻被关**,
界面上看就是"点了没反应"(极易误判成"点击没接上")。 */
embedded: true,
onClose: (): void => { onClose: (): void => {
this.myDetailSheet = false; this.myDetailSheet = false;
}, },
@@ -399,7 +410,10 @@ export struct FbTopBar {
} }
} }
.width('100%') .width('100%')
.constraintSize({ maxHeight: '82%' }) /* ⚠️ 不要给这层加 `constraintSize({maxHeight})`:
sheet 的高度由 `SheetSize.LARGE` 决定,卡片自己已有 `maxHeight:72%`;
再叠一层百分比约束只会让"高度该多高"变成三方打架
(真机症状就是浮层起来立刻被关)。 */
} }
/** /**