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
@@ -22,7 +22,9 @@
*/
import { hilog } from '@kit.PerformanceAnalysisKit';
import { FbColors, FbMetric, FB_HAIRLINE, FB_MASK, FB_SHADOW } from '../common/Theme';
import { FbColors, FbMetric, FB_MASK } from '../common/Theme';
import { FbModuleDef, fbModuleOf, fbThemeVars } from '../model/Modules';
import { FbSheetRing } from './FbSheetRing';
import { FbRosterPerson, FbJobRow, FbContactRow } from '../data/FbPersonRepo';
import { FB_TOKEN_STORE } from '../data/FbTokenStore';
import { FbApiOptions, FbApiResult, fbApi, fbApiBase } from '../data/FbHttp';
@@ -128,6 +130,23 @@ export struct FbPersonEditSheet {
* 没有"必须先建标签"的限制)⇒ 下面给了"新标签"输入框 + 加号。
*/
@Param knownTags: string[] = [];
/**
* 卡片外圈描边环的颜色(hex,'' = 用名册模块色)。
*
* ⚠️ 必须是字符串:环色要直接当背景色(`FbColors.brand` 是 `Resource`,用不了)。
*/
@Param accent: string = '';
/** 环色:显式给了就用给的,否则取名册模块色(与简要卡/详情卡同一个色系) */
private accentColor(): string {
if (this.accent !== '') {
return this.accent;
}
const m: FbModuleDef | undefined = fbModuleOf('people');
return fbThemeVars(m !== undefined ? m.color : '#A84A28').brand;
}
/** 取消 */
@Event onCancel: () => void = (): void => {
};
@@ -519,7 +538,10 @@ export struct FbPersonEditSheet {
})
}
/** 通用小开关(闰月 / 虚拟人物) */
/** 通用小开关(闰月 / 虚拟人物)
* ⚠️ 外层间距用 `margin` 而不是父容器的 `space`:这两个开关在 `Flex(wrap)` 里,
* `Flex` 没有 `space` 参数;margin 在换行后也能保持间距。
*/
@Builder
toggleChip(label: string, on: boolean, onTap: () => void) {
Row({ space: 6 }) {
@@ -531,6 +553,7 @@ export struct FbPersonEditSheet {
.fontColor(on ? Color.White : FbColors.muted)
}
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.margin({ right: 8, bottom: 6 })
.backgroundColor(on ? '#4A7C82' : FbColors.inputBg)
.borderRadius(FbMetric.radiusPill)
.onClick((): void => {
@@ -679,8 +702,14 @@ export struct FbPersonEditSheet {
build() {
// 🔴 外层 `Stack` 是**必需**的:本组件有**两个**要显示的层
// (编辑卡片 + 删除确认遮罩),而 build() 只能有一个根节点。
// ⚠️ `alignContent: Bottom` 让编辑卡片贴底(与详情卡片同一形态)。
Stack({ alignContent: Alignment.Bottom }) {
//
// 🔴 2026-10-19 用户:「编辑,也弄成弹框,加线,保持一致。」
// ⇒ 原来是"贴底满宽 + 只有上面两个角圆",看起来是**抽屉**而不是弹框,
// 和简要卡/详情卡并排切换时观感断层。
// 现在与它们完全同一种形态:**92% 宽 + 四角圆角 + 屏幕居中 + 主题色环**。
// ⚠️ 居中对键盘不友好?——不会:输入框获得焦点时系统会整体上推页面,
// 居中卡片反而不会被键盘压住(贴底卡才会)。真机验证过。
Stack({ alignContent: Alignment.Center }) {
this.editCard()
// ---- 删除确认(页面内遮罩,盖在卡片之上)----
@@ -695,7 +724,11 @@ export struct FbPersonEditSheet {
/** 编辑卡片本体 */
@Builder
editCard() {
Column({ space: 0 }) {
// ⚠️ 外面这一层只给**宽度**(92%):卡片自己必须是 100%,
// 否则 0.92×0.92 ≈ 84.6%,会比简要卡/详情卡窄一圈。
Column() {
FbSheetRing({ radius: FbMetric.radius, ringColor: this.accentColor() }) {
Column({ space: 0 }) {
// ---- 标题 ----
Row({ space: 12 }) {
FbAvatar({
@@ -751,7 +784,14 @@ export struct FbPersonEditSheet {
this.field('生日(农历)', this.fBirthdayLunar, '如 1985-10-05', (v: string): void => {
this.fBirthdayLunar = v;
})
Row({ space: 8 }) {
/* 🔴 2026-10-19:原来是一个 `Row({space:8}).width('100%')` ——
两个小开关并排时,"虚拟人物(还没弄清真实身份)"这一条**放不下**,
Row 只横向排、放不下就**直接溢出**,被卡片 `clip` 裁掉半句话
(真机截图里就是"…还没弄清真实"戛然而止)。
⇒ 换成 `Flex(wrap)`:放不下就**换行**,两个都不丢。
⚠️ 别改成给文字加 `maxLines(1)` + 省略号 —— 那句话是**解释功能**的,
截成"虚拟人物(还没弄清…"等于没解释。 */
Flex({ wrap: FlexWrap.Wrap, alignItems: ItemAlign.Center }) {
this.toggleChip('农历闰月', this.fLunarLeap, (): void => {
this.fLunarLeap = !this.fLunarLeap;
})
@@ -973,11 +1013,17 @@ export struct FbPersonEditSheet {
}
}
.width('100%')
/* ⚠️ 高度上限写在**卡片自己**身上(不是环层):卡片里有
`Scroll().layoutWeight(1)`,它要靠父容器的确定高度算"还能滚多少"。
挪到外面 ⇒ 内层高度变成内容驱动 ⇒ 滚动区塌成一条线。 */
.constraintSize({ maxHeight: '82%' })
.padding({ left: 18, right: 18, top: 18, bottom: 20 + FB_SHEET_BOTTOM_GAP })
.backgroundColor(FbColors.surface)
.borderRadius({ topLeft: FbMetric.radius, topRight: FbMetric.radius })
/* 居中形态**不再需要**底部手势区缓冲(FB_SHEET_BOTTOM_GAP):
卡片不贴屏幕底,手势区碰不到它。 */
.padding({ left: 18, right: 18, top: 18, bottom: 20 })
.alignItems(HorizontalAlign.Start)
}
}
.width('92%')
}
/**
@@ -991,7 +1037,12 @@ export struct FbPersonEditSheet {
@Builder
deleteConfirm() {
Column() {
Column({ space: 8 }) {
/* 🔴 2026-10-19:确认框同样加**主题色环** ——
用户要的是"所有弹框都加线",这一层虽然小,但它也是弹框;
漏掉它就又出现"有的有线、有的没线"的不一致。 */
Column() {
FbSheetRing({ radius: FbMetric.radius, ringColor: this.accentColor() }) {
Column({ space: 8 }) {
Text('删除「' + (this.person !== null ? this.person.shown : '') + '」?')
.fontSize(FbMetric.fontBody)
.fontWeight(FontWeight.Medium)
@@ -1028,17 +1079,13 @@ export struct FbPersonEditSheet {
}
.width('100%')
.margin({ top: 10 })
}
.width('100%')
.padding(18)
.alignItems(HorizontalAlign.Start)
}
}
.width('100%')
.padding(18)
.backgroundColor(FbColors.surface)
.borderRadius(FbMetric.radius)
/* 🔴 v59c:与 `FbPersonSheet` / `FbPersonQuickSheet` 同一套"三层分离"
(用户 2026-10-18:「白色和下面列表的白色融合在一起,看不出是弹框」)。
⚠️ 三个浮层必须**同手段**,否则从简要卡 → 编辑切过去会突然"变平"。 */
.shadow({ radius: 24, color: FB_SHADOW, offsetX: 0, offsetY: 6 })
.border({ width: 1, color: FB_HAIRLINE })
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.height('100%')