v60b:所有弹框统一主题色描边环 + 编辑改居中弹框 + 修名册下拉回弹吃掉悬浮栏

1)9 个浮层统一描边环(抽组件,不复制 8 遍)
   新增 components/FbSheetRing.ets:环宽 FB_RING_W=2 全工程唯一一处;
   实现是"外层 Column 铺色 + padding",不是 border(border 画在盒子内侧);
   外圆角 = 内圆角 + 环宽。简要卡/详情/编辑/日程详情/日程编辑/观影详情/影院/
   日程设置/模块设置全部改用;各浮层的环色 = 所属模块的主题色。

2)编辑从"贴底满宽 + 只有上圆角"改成居中弹框
   人员编辑与日程编辑:Stack 对齐改 Center,卡片 92% 宽 + 四角圆 + 环;
   constraintSize 留在卡片自己身上(挪到外层会让内层 Scroll().layoutWeight
   拿到 0 高度,资料区塌成一条线)。日程的日期时间选择弹层、两个删除确认框
   一并套环,避免同一次交互里出现两种形态。

3)修名册下拉到头回弹后悬浮按钮消失
   EdgeEffect.Spring 在越界松手后把内容弹回,每帧 offset>0 被当成"用户在往上滑"
   ⇒ 收起悬浮栏。判据 = 手指已抬起 + 最后一次方向是下滑 + 抬手不到 500ms;
   拦截必须排在 offset >= HIDE_AT 快路径之前(回弹第一帧位移常 >= 12vp)。

4)真机截图暴露的两个观感问题(静态检查全绿也照错)
   - Scroll().layoutWeight(1) 让短内容也撑满 maxHeight ⇒ 卡片空半屏
     (日程详情/模块设置),改为 flexShrink(1);内容本来就长的浮层不动。
   - 人员编辑"虚拟人物(还没弄清真实身份)"被卡片裁掉半句:两个小开关从
     并排 Row 改为 Flex(wrap) 换行,间距改走 margin。

验证:ArkTS 守卫 444/0;反向验证 17/17;真机 _fb_v60b_verify.py 11/0,
且关掉回弹拦截重编重装后该用例确实变红(证明测试不是假绿);
9 个浮层逐个真机截图确认。
This commit is contained in:
2026-10-19 08:29:44 +08:00
parent 7858de27e3
commit 1a67247407
11 changed files with 1120 additions and 740 deletions
@@ -16,9 +16,11 @@
*/
import { FbColors, FbMetric } from '../common/Theme';
import { FbModuleDef, fbModuleOf, fbThemeVars } from '../model/Modules';
import { FbMovieAttendeeRow, FbMovieDetail } from '../data/FbMovieRepo';
import { FB_TOKEN_STORE } from '../data/FbTokenStore';
import { FbAvatar, FbRating } from './Widgets';
import { FbSheetRing } from './FbSheetRing';
import { fbImageSrc } from '../data/FbAttachQueue';
/**
@@ -46,6 +48,22 @@ export struct FbMovieDetailSheet {
@Event onClose: () => void = (): void => {
};
/**
* 卡片外圈描边环的颜色(hex,'' = 用观影模块色)。
*
* ⚠️ 必须是字符串:环色要直接当背景色(`FbColors.brand` 是 `Resource`,用不了)。
*/
@Param accent: string = '';
/** 环色:显式给了就用给的,否则取观影模块色 */
private accentColor(): string {
if (this.accent !== '') {
return this.accent;
}
const m: FbModuleDef | undefined = fbModuleOf('movies');
return fbThemeVars(m !== undefined ? m.color : '#A84A28').brand;
}
// ---- 全屏大图查看器 ----
/** 查看器是否打开 */
@Local viewerOpen: boolean = false;
@@ -127,13 +145,20 @@ export struct FbMovieDetailSheet {
// **同一个 Stack 里的后声明节点**,不能放进浮层内部的 Column
// (那会被浮层的圆角裁剪、也会被内层 Scroll 的滚动带走)。
Stack({ alignContent: Alignment.Center }) {
Column({ space: 0 }) {
this.sheetHeader()
this.sheetBody()
/* ⚠️ 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%')
.backgroundColor(FbColors.surface)
.borderRadius(FbMetric.radius)
.width('92%')
// ⚠️ 同样用 `Default`(不写)。`Block` 会挡住浮层内部的子节点
// (照片缩略图、关闭按钮都点不动)。
// `Default` 已足够:浮层在遮罩之后声明 ⇒ 是兄弟节点、会被优先命中。
@@ -498,7 +523,9 @@ export struct FbMovieDetailSheet {
// **不要让它左右充满整个屏幕,下面也不要挨着屏幕**,
// 而是放在**屏幕中间**,采用**圆角浮窗**式的形式。」
// ⇒ 不再贴底满宽:左右留边、四角圆角、垂直居中(由宿主的 Stack 对齐)。
.width('92%')
/* ⚠️ 100%(不是 92%):卡片本身已经是 92% 宽,
这里再 92% 就变成 84.6%("卡片比容器窄一圈"的经典错)。 */
.width('100%')
.constraintSize({ maxHeight: '72%' })
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring)