diff --git a/entry/src/main/ets/components/FbMovieDetailSheet.ets b/entry/src/main/ets/components/FbMovieDetailSheet.ets index bbd05af..d390c8a 100644 --- a/entry/src/main/ets/components/FbMovieDetailSheet.ets +++ b/entry/src/main/ets/components/FbMovieDetailSheet.ets @@ -15,7 +15,7 @@ * 加载中 / 成功 / 失败。别只画成功态 —— 断网时用户会看到"空白浮层"。 */ -import { FbColors, FbMetric, fbAlpha } from '../common/Theme'; +import { FbColors, FbMetric, fbAlpha, FB_MASK } from '../common/Theme'; import { FbModuleDef, fbModuleOf, fbThemeVars } from '../model/Modules'; import { FbMovieAttendeeRow, FbMovieDetail } from '../data/FbMovieRepo'; import { FB_TOKEN_STORE } from '../data/FbTokenStore'; @@ -139,67 +139,94 @@ export struct FbMovieDetailSheet { } build() { - // FB_WRAP_CENTER:把卡片**垂直居中**(用户 2026-10-16 要求:浮在屏幕中间)。 - // ⚠️ 居中放在**组件内部**做,而不是改宿主的 Stack 对齐 —— - // 宿主 Stack 里还有别的子元素(日程页的悬浮 +),改对齐会连带挪走它们。 - // ⚠️ 外层铺满 + Center;内层仍是那张 92% 宽的圆角卡。 - Column() { - // 🔴 用 Stack 把"浮层内容"和"全屏查看器"叠起来: - // 查看器要盖住整个浮层(含它自己的圆角/把手),所以必须是 - // **同一个 Stack 里的后声明节点**,不能放进浮层内部的 Column - // (那会被浮层的圆角裁剪、也会被内层 Scroll 的滚动带走)。 - Stack({ alignContent: Alignment.Center }) { - /* ⚠️ 2026-10-19:外面这一层只给宽度(92%)—— - 以前卡片是 `width('100%')`(贴屏幕两边)+ 内部 Scroll 92%, - 看上去是"一整块通栏白",加环也没处可加(环会跑出屏幕)。 - 现在:卡片 92% + 主题色环,内部滚动区 100%(等价于原来的内容宽度)。 */ - Column() { - FbSheetRing({ radius: FbMetric.radius, ringColor: this.accentColor() }) { - Column({ space: 0 }) { - this.sheetHeader() - this.sheetBody() + /* 🔴 用户 2026-11-06:「电影这个框弹出来的时候,应该在下面盖一层蒙版吧? + 跟近期任务类似。」 + + ⇒ 根节点是 **`Stack`**,里面「全屏遮罩 + 卡片」是兄弟节点、叠在一起 —— + 与 `FbTaskPanel`(其文件头第 ③ 条)、`FbDayEditSheet` 完全同一做法。 + + ⚠️ 为什么蒙版做在**组件内部**、而不是宿主外面包一层: + · 组件根节点自己铺满全屏(居中卡片需要),会把"点遮罩关闭"的点击 + 吃在手里 ⇒ 蒙版看着有、点它关不掉; + · 宿主再包一层遮罩就成了两层暗色叠在一起(越点越黑)。 + ⇒ 谁铺满全屏,谁负责画遮罩。这是本项目浮层的一致约定。 + + ⚠️ 遮罩色统一用 `FB_MASK`(`#66000000`,与全站其它浮层一致; + 实测压暗后底噪 247→147,反推 alpha≈0.40 = 0x66/255)。 */ + Stack({ alignContent: Alignment.Center }) { + /* 全屏遮罩:点它就关(与任务面板同一交互) */ + Column() + .width('100%') + .height('100%') + .backgroundColor(FB_MASK) + .onClick((): void => { + this.onClose(); + }) + + /* 卡片容器:铺满 + 居中。 + ⚠️ 居中放在**组件内部**做,而不是改宿主的 Stack 对齐 —— + 宿主 Stack 里还有别的子元素(日程页的悬浮 +),改对齐会连带挪走它们。 + ⚠️ 必须 `HitTestMode.Transparent`:让点击穿透到上层遮罩, + 同时卡片自身的子节点仍可点(`Block` 会让关闭按钮点不动)。 */ + Column() { + // 🔴 用 Stack 把"浮层内容"和"全屏查看器"叠起来: + // 查看器要盖住整个浮层(含它自己的圆角),所以必须是 + // **同一个 Stack 里的后声明节点**,不能放进浮层内部的 Column + // (那会被浮层的圆角裁剪、也会被内层 Scroll 的滚动带走)。 + Stack({ alignContent: Alignment.Center }) { + /* ⚠️ 2026-10-19:外面这一层只给宽度(92%)—— + 以前卡片是 `width('100%')`(贴屏幕两边)+ 内部 Scroll 92%, + 看上去是"一整块通栏白",加环也没处可加(环会跑出屏幕)。 + 现在:卡片 92% + 主题色环,内部滚动区 100%。 */ + Column() { + FbSheetRing({ radius: FbMetric.radius, ringColor: this.accentColor() }) { + Column({ space: 0 }) { + this.sheetHeader() + this.sheetBody() + } + .width('100%') } - .width('100%') + } + .width('92%') + // ⚠️ 同样用 `Default`(不写)。`Block` 会挡住浮层内部的子节点 + // (照片缩略图、关闭按钮都点不动)。 + + // ---- 全屏大图查看器 ---- + if (this.viewerOpen) { + this.viewer() + } + + // ---- 影评 + 评论浮层(工单 #27)---- + if (this.reviewsOpen && this.detail !== null) { + FbMovieReviewSheet({ + movieId: this.detail.id, + movieTitle: this.detail.title, + accent: this.accentColor(), + onClose: (): void => { + this.reviewsOpen = false; + }, + onChanged: (): void => { + // 评分/条数可能变了,但详情接口不返回,不在这里强刷 + } + }) } } - .width('92%') - // ⚠️ 同样用 `Default`(不写)。`Block` 会挡住浮层内部的子节点 - // (照片缩略图、关闭按钮都点不动)。 - // `Default` 已足够:浮层在遮罩之后声明 ⇒ 是兄弟节点、会被优先命中。 - - // ---- 全屏大图查看器 ---- - if (this.viewerOpen) { - this.viewer() - } - - // ---- 影评 + 评论浮层(工单 #27)---- - if (this.reviewsOpen && this.detail !== null) { - FbMovieReviewSheet({ - movieId: this.detail.id, - movieTitle: this.detail.title, - accent: this.accentColor(), - onClose: (): void => { - this.reviewsOpen = false; - }, - onChanged: (): void => { - // 评分/条数可能变了,但详情接口不返回,不在这里强刷 - } - }) - } + .width('100%') + // ⚠️ 高度随内容自适应(浮层是"底部抽屉"形态)。 + // 🔴 但**查看器打开时必须撑满**:查看器要盖住整屏, + // 如果这里还是内容高度,查看器会被裁在一小块里, + // 而且**它没盖住的地方点击会穿透到外层** —— + // 表现就是"点查看器的 ✕,结果把详情浮层也一起关了" + // ( 2026-10-12 实测到的 bug)。 + .height(this.viewerOpen ? '100%' : undefined) } .width('100%') - // ⚠️ 高度随内容自适应(浮层是"底部抽屉"形态)。 - // 🔴 但**查看器打开时必须撑满**:查看器要盖住整屏, - // 如果这里还是内容高度,查看器会被裁在一小块里, - // 而且**它没盖住的地方点击会穿透到外层的遮罩** —— - // 表现就是"点查看器的 ✕,结果把详情浮层也一起关了" - // ( 2026-10-12 实测到的 bug)。 - .height(this.viewerOpen ? '100%' : undefined) + .height('100%') + .justifyContent(FlexAlign.Center) + .hitTestBehavior(HitTestMode.Transparent) } .width('100%') .height('100%') - .justifyContent(FlexAlign.Center) - .hitTestBehavior(HitTestMode.Transparent) } /** 浮层顶部:把手 + 关闭 */ diff --git a/entry/src/main/ets/components/FbPersonSheet.ets b/entry/src/main/ets/components/FbPersonSheet.ets index 83e3627..ca6ff28 100644 --- a/entry/src/main/ets/components/FbPersonSheet.ets +++ b/entry/src/main/ets/components/FbPersonSheet.ets @@ -24,7 +24,7 @@ * 只是缺那几项详情专属字段,界面不会空。 */ -import { FbColors, FbMetric, fbTint, FbTint, fbAlpha } from '../common/Theme'; +import { FbColors, FbMetric, fbTint, FbTint, fbAlpha, FB_MASK } from '../common/Theme'; import { FbModuleDef, fbModuleOf, fbThemeVars } from '../model/Modules'; import { FbRosterPerson, FbJobRow, FbContactRow } from '../data/FbPersonRepo'; import { FbAvatar, FbTag } from './Widgets'; @@ -364,11 +364,27 @@ export struct FbPersonSheet { } build() { - // FB_WRAP_CENTER:把卡片**垂直居中**(用户 2026-10-16 要求:浮在屏幕中间)。 - // ⚠️ 居中放在**组件内部**做,而不是改宿主的 Stack 对齐 —— - // 宿主 Stack 里还有别的子元素(日程页的悬浮 +),改对齐会连带挪走它们。 - // ⚠️ 外层铺满 + Center;内层仍是那张 92% 宽的圆角卡。 - Column() { + /* 🔴 用户 2026-11-06:「电影这个框弹出来的时候,应该在下面盖一层蒙版吧? + 跟近期任务类似。」 + ⇒ 同一条要求同步到这里:首页「联系人」点开的这张卡原来也没有遮罩。 + + ⚠️ 蒙版做在**组件内部**(谁铺满全屏谁负责画遮罩),宿主不再包一层: + 包两层会越点越黑,而且外层遮罩会被组件自己的全屏根节点吃掉点击。 + ⚠️ `embedded` 时不画遮罩:那种情况宿主是 `bindSheet`, + 系统已经给了自己的遮罩,再叠一层就是双暗。 */ + Stack({ alignContent: Alignment.Center }) { + if (!this.embedded) { + Column() + .width('100%') + .height('100%') + .backgroundColor(FB_MASK) + .onClick((): void => { + this.onClose(); + }) + } + + /* ↓↓↓ 以下为原来的根 Column(内容一字未改,只是套进了 Stack) ↓↓↓ */ + Column() { // ⚠️ 2026-10-19:**卡片外面那一圈主题色环**由共用组件 `FbSheetRing` 给 // (用户要求"所有弹框都加线,保持一致")。 // 这一层只负责**尺寸**(92% 宽 / 72% 高上限)—— 尺寸与外观分开, @@ -630,6 +646,9 @@ export struct FbPersonSheet { .width(this.embedded ? 'auto' : '100%') .height(this.embedded ? 'auto' : '100%') .justifyContent(this.embedded ? FlexAlign.Start : FlexAlign.Center) - .hitTestBehavior(HitTestMode.Transparent) + .hitTestBehavior(HitTestMode.Transparent) + } + .width('100%') + .height('100%') } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/home/FbHomePage.ets b/entry/src/main/ets/pages/home/FbHomePage.ets index 25d22a8..c50c833 100644 --- a/entry/src/main/ets/pages/home/FbHomePage.ets +++ b/entry/src/main/ets/pages/home/FbHomePage.ets @@ -2125,7 +2125,14 @@ export struct FbHomePage { /* 🔴 用户:「"最近观影"点了之后没反应…点了之后应该有反应, 要么弹出来详细信息。」 ⚠️ 复用电影页的同一张卡(`FbMovieDetailSheet`), - 不为首页另写一套 —— 否则改一处要改两处。 */ + 不为首页另写一套 —— 否则改一处要改两处。 + + 🔴 用户 2026-11-06:「电影这个框弹出来的时候,应该在下面盖一层蒙版吧? + 跟近期任务类似。」 + ⇒ 蒙版由**组件自己**提供(`FbMovieDetailSheet` 内部根 Stack: + 遮罩 + 卡片叠着),与 `FbTaskPanel` / `FbDayEditSheet` 同一做法。 + **宿主这里不要再包一层** —— 包了就是两层暗色叠在一起(越点越黑), + 而且点遮罩时会被组件自己的全屏根节点拦掉,关不掉。 */ if (this.mvDetail !== null || this.mvDetailLoading || this.mvDetailError !== '') { FbMovieDetailSheet({ @@ -2145,7 +2152,10 @@ export struct FbHomePage { // ==================== #64:联系人详情(可打电话)==================== /* 🔴 用户:「点击之后弹出来详细信息,就跟名册里面那个人的列表 点击完之后的效果是一样的,这样可以直接打电话。」 - ⚠️ 复用名册那张卡(`FbPersonSheet`),它自带手机号拨号入口。 */ + ⚠️ 复用名册那张卡(`FbPersonSheet`),它自带手机号拨号入口。 + + 🔴 2026-11-06 同一条:蒙版同样由组件自己提供(`FbPersonSheet` 内部根 + Stack:遮罩 + 卡片)。宿主不包 —— 理由同上一段。 */ if (this.pfDetail !== null) { FbPersonSheet({ person: this.pfDetail,