v74 端上删掉家谱 + 名册页头改「醒目搜索 + 添加」

用户 2026-10-21:「算了,你画不好。这个功能直接从鸿蒙端去掉吧,不要了。
这样名册这个功能,进来以后,上面就是搜索和添加按钮,搜索,做的醒目一点,
现在看不清。家谱和名册的标签,都不要了。家谱的相关功能,都删除。」

家谱链路整条删除(不是隐藏):
- 删 common/KinLayout.ets、components/FbKinGraph.ets 两个源文件
- 删 Types 的 FbKinPerson / FbKinGraph / FbKinRelation、FbParse 的 parseKin
- 删 FbModuleLoad 路由、FbModuleRepo 模块名、FbSyncAll 预同步清单里的 kin
- 删 DemoData 的 FB_DEMO_KIN_RELATIONS、Theme/Settings 的家谱图标映射
- 删死资源 fb_ic_kin.png
- Index 的 kin 分支去掉(不再有「名册/家谱」两个模块共用一个页面)
- FbPeoplePage:删 subView 分段切换、kinHeader/kinView、loadKin、segBtn;
  openDetail 不再借家谱字段兜底,改成纯 id 占位 + 异步补全
  (保留"本地没缓存也要立刻开浮层",那是 v73 修过的"点了没反应")

名册页头:
- 第一行就是搜索 + 添加,上面不再有任何东西
- 搜索框从「看不清」改成醒目:白底 #FFFFFF(原来 inputBg 与页面米色几乎同色
  ⇒ 没有边界)、1.5 主题色描边、高度 36→44、图标 15→18 并染主题色、
  字号 fontSmall→fontBody、提示词收短成「搜索姓名 / 首字母 / 手机」

验证:
- ArkTS 静态守卫 917/0(新增 6 条:家谱五条链路全清 + 搜索框三个具体数值)
- 反向验证 154/0(新增 7 条注入全部有效;另修好两条因代码演进失效的旧锚点)
- 编译 BUILD SUCCESSFUL + 装机 install bundle successfully
- 真机 v74 探针 22/0(高度实测 44.2vp、白底、占屏 66%、同排对齐、
  家谱在页面与抽屉里都不存在、搜索输入与清空真的生效)

顺带修掉探针三处假红(都不是应用问题,是量错了节点):
- 搜索框在界面树里是两层,TextInput(75px) 只是内部输入区,
  白底容器 Row(138px) 才是盒子;只按"有文字/可点"收集会把它漏掉
- uitest uiInput inputText 是 (x,y,text) 三参数,只给文本会静默不输入
- 空态文案是「换个关键词试试…」,按"没有/找到"判永远为假
This commit is contained in:
2026-10-21 23:28:19 +08:00
parent 8cabc35400
commit 6e7cdaf5fc
14 changed files with 150 additions and 1615 deletions
+1 -1
View File
@@ -647,7 +647,7 @@ struct Index {
moduleContent() {
if (this.app.activeModule === 'home') {
FbHomePage({ app: this.app, topBar: this.topBarSlot })
} else if (this.app.activeModule === 'people' || this.app.activeModule === 'kin') {
} else if (this.app.activeModule === 'people') {
FbPeoplePage({ app: this.app, topBar: this.topBarSlot })
} else if (this.app.activeModule === 'movies') {
FbMoviesPage({ app: this.app, topBar: this.topBarSlot })
+142 -289
View File
@@ -1,28 +1,25 @@
/**
* 家书 FamilyBook · 名册 / 家谱(鸿蒙端)
* 家书 FamilyBook · 名册(鸿蒙端)
*
* 对应 Web 端 `templates/people/index.php`(名册)与 `templates/kin/tree.php`(家谱)。
* 对应 Web 端 `templates/people/index.php`。
*
* 这两页在 Web 端是两个模块,但**看的是同一批人**:名册是「平铺的名单」(通讯录视角),
* 家谱是「以我为中心的连线」(关系视角)。端上放在同一个文件里,用内部分段切换 ——
* 手机上在两页之间来回跳没有意义,切一下就能对照看。
*
* ⚠️ 家谱的树形图(Web 端是 HTML 折线)在手机窄屏上排不下。
* 端上改成**「以我为中心的三圈」**:我 → 直系(父母/配偶/子女)→ 其他亲属,
* 每圈一张卡,卡里按人列。关系表达上等价,宽度可控。
* 🔴 v74(用户 2026-10-21):「这个功能直接从鸿蒙端去掉吧,不要了。
* 这样名册这个功能,进来以后,上面就是搜索和添加按钮……
* 家谱和名册的标签,都不要了。家谱的相关功能,都删除。」
* ⇒ **家谱在端上彻底删除**(页面内分段切换、图谱视图、`/api/v1/kin` 取数、
* 同步清单里的 `kin` 项全都没了)。现在这一页只有名册。
* ⚠️ Web 端的家谱页**不受影响**(用户说的是"鸿蒙端")——
* 服务端 `templates/kin/`、`Kinship` 那套仍然照常工作。
*/
import { FbColors, FbMetric, fbTint, FbTint, FB_MASK } from '../../common/Theme';
import { FbAppState, fbAppState } from '../../common/Store';
import { FbKinGraph, FbKinPerson, FbRosterItem } from '../../model/Types';
import { FbRosterItem } from '../../model/Types';
import { FbModuleDef, fbModuleOf, fbThemeVars } from '../../model/Modules';
import { parseKin } from '../../data/FbParse';
import { loadModule } from '../../data/FbModuleLoad';
import { FbAvatar, FbPrimaryButton, FbTag } from '../../components/Widgets';
import { FbPersonSheet } from '../../components/FbPersonSheet';
import { FbPersonQuickSheet } from '../../components/FbPersonQuickSheet';
import { FbPersonEditSheet } from '../../components/FbPersonEditSheet';
import { FbKinGraphView } from '../../components/FbKinGraph';
import { FbTagChip } from '../../components/FbTagChip';
import { FB_DOCK_RESERVE } from '../../components/FloatDock';
import { FbDb } from '../../data/FbDb';
@@ -183,8 +180,6 @@ export struct FbPeoplePage {
@BuilderParam topBar: () => void;
@Param app: FbAppState = fbAppState();
/** 子视图:名册(roster)/ 家谱(kin) */
@Local subView: string = 'roster';
/** 搜索词(本地过滤) */
@Local keyword: string = '';
/**
@@ -300,23 +295,6 @@ export struct FbPeoplePage {
*/
private rawById: Map<number, FbRosterPerson> = new Map<number, FbRosterPerson>();
/**
* 家谱里的人(**来自接口** `/api/v1/kin`)。
*
* ⚠️ 与名册的 `all` **是两批数据**:
* 名册是"我名册里的人"(可上千),家谱是"以我为中心的关系网"(上下两代、十来人)。
* 所以家谱要**单独拉**,不能从 `all` 里挑。
*/
@Local kinPeople: FbKinPerson[] = [];
/** 家谱的同步提示(与名册分开:两批数据可能一个成功一个失败) */
@Local kinSyncNote: string = '';
/**
* ⚠️ v73:原来这里有 `kinMode`('graph' / 'list' 两种视图)。
* 用户 2026-10-21 要求去掉列表、只留图谱 ⇒ 状态本身也不用了。
* 「列表」那套是按代数分组的通讯录,名册页已经有同样的能力。
*/
/** 我在家谱里的 id(图谱里要标出「我」) */
@Local kinMeId: number = 0;
private repo: FbPersonRepo = FbPersonRepo.inst();
/**
@@ -378,12 +356,7 @@ export struct FbPeoplePage {
this.quickFromDetail = null;
return true;
}
// ④ 家谱 ⇒ 回名册(同一页的两个子视图,"返回上一层"就是回到默认那个)
if (this.subView === 'kin') {
this.subView = 'roster';
return true;
}
// ⑤ 本页没有可关的东西 ⇒ 交给 `Index`(换回上一个模块)
// ④ 本页没有可关的东西 ⇒ 交给 `Index`(换回上一个模块)
return false;
}
@@ -491,13 +464,6 @@ export struct FbPeoplePage {
* "这次没更新",用户手上的数据仍然可用。只在页面上留一行小字。
*/
private async syncInBackground(): Promise<void> {
// 🔴 家谱与名册**并行拉**:两者是**独立的两批数据**
// (名册 = 我名册里的人,可上千;家谱 = 以我为中心的上下两代,十来人)。
// ⚠️ 并行(不是串行):串行会让"切到家谱"多等一个往返。
// ⚠️ 家谱**不 await 在名册后面**:名册走实体表、家谱走通用缓存,
// 两条路互不依赖。
this.loadKin();
const token: string = FB_TOKEN_STORE.get();
if (token === '') {
this.syncNote = '未登录,显示的是本地数据';
@@ -613,25 +579,6 @@ export struct FbPeoplePage {
}
}
/**
* 拉家谱(`/api/v1/kin`)—— 先缓存、再网络。
*
* ⚠️ 家谱走**通用缓存**(`module_cache`)而不是实体表:
* 它是"一张关系网",形状与名册的"一批人"不同,
* 落成实体表要 4 张(人/父母/配偶/子女),收益却只是
* "省一次解析"——不值得。见 `FbModuleRepo` 的说明。
*/
private async loadKin(): Promise<void> {
await loadModule<FbKinGraph>('kin', parseKin,
(g: FbKinGraph): void => {
this.kinPeople = g.people;
this.kinMeId = g.meId;
},
(note: string): void => {
this.kinSyncNote = note;
});
}
build() {
// 🔴 外层再套一个 Stack:把"页面内容"与"人员详情浮层"叠起来。
// 浮层是**页面内的覆盖层**(不是系统弹窗),理由见 FbPersonSheet 文件头。
@@ -840,13 +787,9 @@ export struct FbPeoplePage {
// ⇒ 解法:**把顶栏与页头塞进 `List` 的第一个 item**,
// 它们就成了"列表内容的一部分",随列表一起滚走;
// 同时 `List` 仍是唯一滚动容器 ⇒ 按需渲染、性能不丢。
// (见 rosterView / kinView 里的 `this.pageHeaderItem()`)
// (见 rosterView 里的 `this.pageHeaderItem()`)
Column() {
if (this.subView === 'roster') {
this.rosterView()
} else {
this.kinView()
}
this.rosterView()
}
.width('100%')
.height('100%')
@@ -880,80 +823,101 @@ export struct FbPeoplePage {
* 页头(分段切换 + 搜索 + 标签筛选条)。
*
* ⚠️ 从 `rosterView` 里提出来放在**页面级**:
* 这样"名册/家谱"与搜索框在两个子视图下都常驻,
* 切换子视图时不会跟着列表一起滚走。
* 这样搜索框在滚动中常驻在列表内容里。
*
* 🔴 v74(用户 2026-10-21):
* · 「家谱和名册的标签,都不要了」⇒ 整条分段切换删掉,
* 连同"当前子视图"那个状态与它的切换按钮生成器一并移除。
* · 「进来以后,上面就是搜索和添加按钮」⇒ 页头**第一行就是搜索 + 添加**,
* 不再有任何东西排在它前面。
* · 「搜索,做的醒目一点,现在看不清」⇒ 见下面 `searchBox` 的说明:
* 高度 36→44、底色从 `inputBg`(几乎与页面同色)改成**白底 + 主题色描边**、
* 图标 15→18 并改用主题色、字号 fontSmall→fontBody、提示词去掉冗长的能力罗列。
*/
@Builder
peopleHeader() {
Column() {
// ---- 分段切换(名册 / 家谱)----
//
// 🔴 v59c(用户 2026-10-18):「名册、家谱这两个标签,用的不是主题色。」
// ⇒ 原来两个都硬编码 `'#A84A28'`(**品牌砖红**),而名册的主题色是
// `FB_MODULES` 里那个 `#4A7C82`(青)—— 页面里其它地方
// (如列表项那个"详情"按钮的底色/字色)用的都是主题色粉调,
// 于是这一处显得"跟整个页面不是一个色系"。
// ⚠️ 用 `fbThemeVars(...).brand` 走统一算法,而不是直接把 `#4A7C82`
// 抄进来:`fbThemeVars` 会把色相拉到一个**可读的饱和度/亮度区间**
// (见其实现),直接抄模块色在选中态当底、白字压上去会偏亮发飘。
// ⚠️ 两处都必须传同一个值 —— 之前正是"两处各写一遍字面量"才漂的。
Row({ space: 4 }) {
this.segBtn('名册', 'roster')
this.segBtn('家谱', 'kin')
}
.padding(3)
.backgroundColor(FbColors.inputBg)
.borderRadius(FbMetric.radiusPill)
.margin({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12 })
if (this.subView === 'roster') {
// ---- 搜索 + 添加 ----
// ---- 搜索 + 添加(页头第一行,上面不再有任何东西)----
Row({ space: 10 }) {
// ============================================================
// 搜索框:**白底 + 主题色细描边 + 略深的内阴影观感**
// ============================================================
//
// 🔴🔴 v74(用户 2026-10-21):「搜索,做的醒目一点,现在看不清」。
//
// 原来为什么"看不清"(三个原因叠在一起):
// ① 底色用 `FbColors.inputBg` —— 它和页面米色 `FbColors.paper`
// 几乎同色,搜索框**没有边界**,看上去就是一段空白;
// ② 高度只有 36,图标 15、字 fontSmall —— 在一屏 2444px 的真机上
// 它占的比例很小,视线扫过去是"灰灰的一条";
// ③ 提示词写着「搜索:姓名 / 拼音首字母(yyq)/ 手机 / 单位」,
// 20 多字塞进 36 高的框里 ⇒ 强制缩小字号 / 显示不全,
// 信息过载反而更难读。
//
// ⇒ 四件事一起改(缺一件都还是不够醒目):
// · **白底**(`#FFFFFF`)—— 页面是米色,白底天然"浮"起来;
// · **1.5 描边 + 主题色**(`#4A7C82` 青)—— 有明确的边界;
// · **高度 44**(≈ 44×3.125 = 138px),接近系统搜索框的舒适高度;
// · **字号 fontBody + 图标 18 且染主题色** —— 一眼能认出"这是搜索"。
// ⚠️ 提示词收短成「搜索姓名 / 首字母 / 手机」,能力说明挪到
// `searchHint()` 的注释里 —— 框里只留能一眼读完的东西。
Row({ space: 8 }) {
Row({ space: 6 }) {
Image($r('app.media.fb_ic_search'))
.width(15)
.height(15)
.renderMode(ImageRenderMode.Template)
.colorFilter(fbTint(FbTint.faint))
TextInput({ placeholder: '搜索:姓名 / 拼音首字母(yyq)/ 手机 / 单位', text: this.keyword })
.placeholderColor(FbColors.faint)
.placeholderFont({ size: FbMetric.fontSmall })
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.ink)
.backgroundColor(Color.Transparent)
.padding(0)
.layoutWeight(1)
.onChange((v: string) => {
this.keyword = v;
})
/* 🔴 v59e:搜索框右侧补一个「清空」——
名册上千人,搜错了要逐字删掉很烦(而且手机上退格容易连删)。
⚠️ 命中区给 24×24(图标只有 12):手机上 12vp 的点击区域点不中。 */
if (this.keyword !== '') {
Row() {
Image($r('app.media.fb_ic_close'))
.width(12)
.height(12)
.colorFilter(fbTint(FbTint.faint))
}
.width(24)
.height(24)
.justifyContent(FlexAlign.Center)
.onClick((): void => {
this.keyword = '';
})
Image($r('app.media.fb_ic_search'))
.width(18)
.height(18)
.renderMode(ImageRenderMode.Template)
// 图标用**名册主题色**(不是原来的 faint 灰)—— 让"搜索"这个动作有颜色。
// ⚠️ 用 `this.themeBrand()` 而不是 `FbTint.brand`:后者是品牌砖红
// (#A84A28),与名册的青色主题不是一个色系(v59c 踩过同样的问题)。
.colorFilter(fbTint(this.themeBrand()))
TextInput({ placeholder: '搜索姓名 / 首字母 / 手机', text: this.keyword })
.placeholderColor(FbColors.muted)
.placeholderFont({ size: FbMetric.fontBody })
.fontSize(FbMetric.fontBody)
.fontColor(FbColors.ink)
.backgroundColor(Color.Transparent)
.padding(0)
.layoutWeight(1)
.onChange((v: string) => {
this.keyword = v;
})
/* 🔴 v59e:搜索框右侧补一个「清空」——
名册上千人,搜错了要逐字删掉很烦(而且手机上退格容易连删)。
⚠️ 命中区给 28×28(图标只有 14):手机上 14vp 的点击区域点不中。
v74 跟着搜索框放大一档(24→28),否则在 44 高的框里显得太小。 */
if (this.keyword !== '') {
Row() {
Image($r('app.media.fb_ic_close'))
.width(14)
.height(14)
.colorFilter(fbTint(FbTint.muted))
}
.width(28)
.height(28)
.justifyContent(FlexAlign.Center)
.onClick((): void => {
this.keyword = '';
})
}
.layoutWeight(1)
.height(36)
.padding({ left: 12, right: 12 })
.backgroundColor(FbColors.inputBg)
.borderRadius(FbMetric.radiusS)
.alignItems(VerticalAlign.Center)
}
.layoutWeight(1)
.height(44)
.padding({ left: 14, right: 10 })
.backgroundColor('#FFFFFF')
// radiusCard=14:比内嵌小块(10)圆润、比区块卡(16)收得住,
// 正好是"一个独立的输入控件"该有的圆角。
.borderRadius(FbMetric.radiusCard)
.border({ width: 1.5, color: this.themeBrand() })
.alignItems(VerticalAlign.Center)
/* 添加按钮:跟着搜索框一起放到 44 高,两颗控件等高才不显歪。
⚠️ `FbPrimaryButton` 自己按内容高度走(约 34),
这里用 `height(44)` 的容器包一层把它撑到同高 ——
直接把 44 塞给组件会破坏它内部的 padding 比例。 */
Column() {
FbPrimaryButton({
label: '添加',
bg: '#4A7C82',
bg: this.themeBrand(),
icon: $r('app.media.fb_ic_plus'),
// 🔴 v52:接上(之前这颗按钮**没有回调**,点了什么都不发生 ——
// "全功能客户端"不能有这样的摆设)。
@@ -962,13 +926,15 @@ export struct FbPeoplePage {
}
})
}
.width('100%')
.padding({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12, bottom: 8 })
.alignItems(VerticalAlign.Center)
// ---- 标签筛选条 ----
this.tagBar()
.height(44)
.justifyContent(FlexAlign.Center)
}
.width('100%')
.padding({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 14, bottom: 10 })
.alignItems(VerticalAlign.Center)
// ---- 标签筛选条 ----
this.tagBar()
}
.width('100%')
}
@@ -1372,18 +1338,18 @@ export struct FbPeoplePage {
* ⚠️ 用 `this.detailToken` 防止"快速连点两个人,前一个的响应把后一个覆盖"。
*/
private openDetail(id: number): void {
/* 🔴🔴 v73 真 bug:点家谱卡片**静默没反应**。
/* 🔴🔴 v73 真 bug:点卡片**静默没反应**(当时是家谱卡片,现在名册卡片同理)。
根因:这里原来只从 `rawById` 取,而 `rawById` 来自 `repo.list()`
—— 那条 SQL 带 `JOIN person_roster`,**只包含"我挑进名册的人"**。
家谱里的人(父母、岳父母、配偶)**不一定在我的名册里** ⇒ 查不到 ⇒ 直接 return。
名册外的人(从分享/搜索进来的人)**不一定在我的名册里** ⇒ 查不到 ⇒ 直接 return。
字面上就是"点了没反应",而且不打日志的话 hilog 一个字都抓不到。
修法分两段(与 `byId()` 的注释同一条思路):
① 名册里有 ⇒ 立刻用本地那份开浮层(不等网络,点开就有内容);
② 名册里没有 ⇒ 先空着把浮层**打开**(`detailPerson` 用一个占位),
② 名册里没有 ⇒ 先开一个**只有 id** 的占位把浮层打开,
再异步 `byId()` / 网络详情补上 —— **不能**因为本地没缓存就不响应点击。
⚠️ 注意 `detailPerson` 是 `FbRosterPerson`,不能塞 `null` 当作"只开框",
所以第 ② 种情况必须先构造一个最小可渲染对象(只有 id + 姓名)。
所以第 ② 种情况必须先构造一个最小可渲染对象。
*/
const cached: FbRosterPerson | undefined = this.rawById.get(id);
hilog.info(DOMAIN, TAG, 'openDetail(%{public}d):名册命中=%{public}s(名册共 %{public}d 人)',
@@ -1395,14 +1361,13 @@ export struct FbPeoplePage {
if (cached !== undefined) {
this.detailPerson = cached;
} else {
// ② 名册里没有 —— 先用家谱那份拼一个最小对象把浮层打开
const kin: FbKinPerson | undefined = this.kinPeople.find(
(p: FbKinPerson): boolean => p.id === id);
if (kin === undefined) {
hilog.warn(DOMAIN, TAG, 'openDetail(%{public}d):名册与家谱都没有这个人', id);
return;
}
this.detailPerson = placeholderPerson(kin);
/* ② 本地名册里没有 —— 仍然立刻把浮层打开,先去 `byId()` 捞一份。
🔴 v74:家谱已从端上删除,原来"用家谱那份拼占位"的兜底没有了;
这里退化成**纯占位**(只有 id),随后 `repo.byId()` / `fetchDetail()`
任一路回来都会整份替换掉它。
⚠️ 不能因为"本地没缓存"就 return —— 那正是 v73 那个"点了没反应"的病。 */
hilog.info(DOMAIN, TAG, 'openDetail(%{public}d):本地名册未命中,先开占位浮层', id);
this.detailPerson = placeholderPerson(id);
}
const token: string = FB_TOKEN_STORE.get();
@@ -1502,126 +1467,21 @@ export struct FbPeoplePage {
}
// ============================================================
// 家谱
// ============================================================
/**
* 家谱的说明条(「以我为中心」+ 人数)。
*
* 🔴 v68:这里原来还有一颗「补关系」按钮,**从来没有 `onTap`** ——
* 点了一点反应都没有(全仓库扫过,只剩这一颗)。
* 它要的是"选一个还没有亲属关系的人 → 给他挂上父/母/配偶",
* 而服务端 `relations` 表**只有读**(没有写接口),网页版也没有这个功能
* ⇒ 与其留一个永远点不动的按钮,先撤掉(免得被当成"坏了")。
* 要做得先加 `POST /api/v1/persons/<id>/relations`,那是独立一批活。
*
* 🔴 v73(用户 2026-10-21):右侧的「图谱 / 列表」切换**整块撤掉**。
* 原话是"手机端这个家谱实在是很难看",而那个"列表"视图
* 本质就是个按代数分组的通讯录 —— 名册页早就有同样的能力
* (还能搜索、筛选标签),留着它只会让人以为家谱有两种画法。
* 现在家谱只有一种形态:**图**。
*/
@Builder
kinHeader() {
Row({ space: 8 }) {
Column({ space: 2 }) {
Text('以我为中心')
.fontSize(FbMetric.fontBody)
.fontWeight(FontWeight.Medium)
.fontColor(FbColors.ink)
Text(this.kinTotalText())
.fontSize(FbMetric.fontTiny)
.fontColor(FbColors.faint)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.width('100%')
.padding({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12, bottom: 8 })
.alignItems(VerticalAlign.Center)
}
@Builder
kinView() {
Column() {
this.pageHeaderItem()
this.kinHeader()
if (this.loading) {
Text('正在读取本地数据…')
.width('100%')
.padding({ top: 40, bottom: 40 })
.textAlign(TextAlign.Center)
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.faint)
} else if (this.kinPeople.length === 0) {
Text('还没有亲属关系。\n在网页端「家谱」里填上父母 / 配偶 / 子女,这里就会长出来。')
.width('100%')
.padding({ top: 40, bottom: 40, left: 24, right: 24 })
.textAlign(TextAlign.Center)
.fontSize(FbMetric.fontSmall)
.fontColor(FbColors.faint)
} else {
// ---- 家谱图(Canvas 画线 + 原生卡片,见 components/FbKinGraph.ets)----
Column() {
FbKinGraphView({
people: this.kinPeople,
meId: this.kinMeId,
onPick: (id: number): void => {
this.openDetail(id);
}
})
}
.layoutWeight(1)
.width('100%')
.padding({ left: FbMetric.pagePad, right: FbMetric.pagePad, bottom: FB_DOCK_RESERVE - 24 })
}
}
.width('100%')
.layoutWeight(1)
}
/**
* 一代人(家谱里的一层)。
*
* ⚠️ v73:这个 Builder 与 `kinRow` 一起**已删除** —— 它们只服务
* 「列表」视图,而用户 2026-10-21 要求去掉列表、只留图谱。
* 原来这里按代数分组、每组一个白卡片 + 行分隔线,
* 本质就是个通讯录;名册页已经有同样的能力(还能搜索/筛标签)。
*/
// ============================================================
// 共用小件
// ============================================================
/**
* 分段切换的一个按钮(名册 / 家谱)。
* 名册模块的主题色。
*
* 🔴 v59c(用户 2026-10-18):「名册、家谱这两个标签,用的不是主题色。」
* ⇒ 颜色**不再由调用方传字面量**,改成方法内部自己取名册模块的主题色。
* 之前两个调用点各写一遍 `'#A84A28'`(品牌砖红,与名册的青色主题不符),
* 而且"两处各写一遍"本身就是漂移源(改一处忘一处)。
* ⚠️ 取色走 `fbThemeVars(名册模块色).brand`(统一算法),
* 不是直接抄 `FB_MODULES` 里那个 `#4A7C82` —— 见调用点上方那段说明。
* 🔴 原来叫「选中态实底」,服务的是「名册 / 家谱」分段切换;
* v74 分段切换删掉了,这个方法**留着**是因为搜索框的描边、搜索图标染色、
* 添加按钮底色都用它 —— 一处取值,三处不漂。
* ⚠️ 取色走 `fbThemeVars(名册模块色).brand`(统一算法),
* 不是直接抄 `FB_MODULES` 里那个 `#4A7C82`:
* `fbThemeVars` 会把色相拉到可读的饱和度/亮度区间,
* 直接抄模块色当实底、白字压上去会偏亮发飘。
*/
@Builder
segBtn(label: string, key: string) {
Text(label)
.fontSize(FbMetric.fontSmall)
.fontWeight(this.subView === key ? FontWeight.Medium : FontWeight.Normal)
.fontColor(this.subView === key ? Color.White : FbColors.muted)
.textAlign(TextAlign.Center)
.height(30)
.layoutWeight(1)
.backgroundColor(this.subView === key ? this.themeBrand() : Color.Transparent)
.borderRadius(FbMetric.radiusPill)
.onClick(() => {
this.subView = key;
})
}
/** 名册模块的主题色(选中态实底)。取色规则见 `segBtn` 的说明。 */
private themeBrand(): string {
const m: FbModuleDef | undefined = fbModuleOf('people');
return fbThemeVars(m !== undefined ? m.color : '#A84A28').brand;
@@ -1665,14 +1525,6 @@ export struct FbPeoplePage {
代数分层的信息现在由**图谱本身的纵向位置**表达(长辈在上、晚辈在下),
不再需要「父母辈 / 我这一辈」这种文字小标题。 */
/** 家谱顶部的人数说明 */
private kinTotalText(): string {
if (this.kinPeople.length === 0) {
return '还没有亲属关系';
}
return this.kinPeople.length.toString() + ' 位亲属 · 上下两代';
}
/** 取某个人的头像色(从名册里查,查不到用默认砖红) */
private colorOf(id: number): string {
const hit: FbRosterItem | undefined = this.all.find((p: FbRosterItem): boolean => p.id === id);
@@ -1681,27 +1533,28 @@ export struct FbPeoplePage {
}
/**
* 用「家谱里的那个人」拼一个**最小可渲染**的 `FbRosterPerson`。
* 拼一个**最小可渲染**的 `FbRosterPerson`(只有 id + 姓名)。
*
* 🔴 为什么需要(v73 真 bug):家谱里的人**不一定在我名册里**
* (`FbPersonRepo.list()` 带 `JOIN person_roster`,只含"我挑进名册的人"),
* 那时点卡片以前是**静默 return ⇒ 点了没反应**。
* 这里先拼一个只够画浮层头部的对象,**让浮层立刻打开**,
* 随后 `repo.byId()` / `fetchDetail()` 到了再原地升级。
* 🔴 为什么需要(v73 真 bug,v74 保留):`FbPersonRepo.list()` 带 `JOIN person_roster`,
* 只包含"我挑进名册的人";而详情接口能取到**名册外**的人(例如从别处跳进来的人)。
* 以前本地查不到就直接 return ⇒ 字面上"点了没反应",hilog 里一个字都没有。
* ⇒ 先拼一个只够画浮层头部的对象,**让浮层立刻打开**,
* 随后 `repo.byId()` / `fetchDetail()` 到了再原地升级。
*
* ⚠️ 只填家谱真有的那几项,其余给空值 —— 不要瞎猜(填错比空着更糟:
* 用户会以为"系统里有这个资料")。本地库/接口补上来后会被整份替换掉。
* ⚠️ v74 起**不再从家谱借字段**(家谱功能已从端上删除):只填 id 与姓名,
* 其余一律给空值 —— 不要瞎猜(填错比空着更糟:用户会以为"系统里有这个资料")。
* 本地库/接口补上来后会被整份替换掉。
*/
function placeholderPerson(k: FbKinPerson): FbRosterPerson {
function placeholderPerson(id: number): FbRosterPerson {
const p: FbRosterPerson = {
id: k.id,
name: k.name,
shown: k.shown !== '' ? k.shown : k.name,
// 家谱那份的称谓是服务端算好的,这里能用
label: k.label,
id: id,
name: '',
shown: '',
// 称谓由服务端按家族关系算(端上没有家谱了,取不到)⇒ 留空
label: '',
tags: [],
avatarColor: k.avatarColor,
avatar: k.avatar,
avatarColor: '#A84A28',
avatar: '',
birthdaySolar: '',
birthdayFull: '',
birthdayLunar: '',
@@ -682,7 +682,6 @@ export struct FbSettingsPage {
private iconOf(key: string): Resource {
switch (key) {
case 'home': return $r('app.media.fb_ic_home');
case 'kin': return $r('app.media.fb_ic_kin');
case 'people': return $r('app.media.fb_ic_people');
case 'movies': return $r('app.media.fb_ic_movies');
case 'trips': return $r('app.media.fb_ic_trips');