首页联系人:修头像显示 + 排除自己 (#88)

用户:首页联系人,需要带头像,排除自己。

① 头像(真 bug:从来没显示出来过)
   改前 Image(p.avatar) —— 而 p.avatar 存的是**存储键**(avatars/xxx.jpg),
   不是能喂给 Image() 的地址。Image("avatars/xxx.jpg") 被当成相对沙箱路径,
   静默失败 ⇒ 永远只剩首字色块,看起来像"这个功能没做"。
   正确出口项目里早就有:fbImageSrc(key, token)(先查本地解密缓存,
   没缓存才拼 media.php?k=&token=)。名册页/观影卡都这么写,唯独首页这处漏了。

   ⇒ 改用 FbAvatar({ name, color, dia, imageUrl }):
     · 底色+首字常驻,图片成功才盖上去 —— 名册绝大多数人没头像,必须回落;
     · 有 key 但图坏了(OSS 文件缺失/token 过期)时 Image 是纯空白,
       FbAvatar 会露出下面的首字,不会变成一块空光板。
   personInitial() 因此成了死代码,删掉(FbAvatar 内部按 name 取首字)。

② 排除自己
   FbPersonRepo.list() 返回整个名册(含"我"),自己占掉一个最显眼的位置
   (一屏只露 6 个,3 列 × 2 行)。
   - 用 session.me.id 判,不用名字/昵称比(重名、改名会漏);
   - 取不到 id(0)时不剔任何人 —— 宁可多显示一个,
     也不要因空 id 把列表清空(本项目踩过判断写反 ⇒ 列表恒空的坑);
   - pfTotal 用剔除后的数量:它渲染成标题「N 人」,
     用原始总数会写 800 人却只列 799 个,看着像丢人。

验证(真机 5NC0225B27004718):
- 标题 800 人 → 799 人;6 格从「爸爸/妈妈/岳父/杨永全/红围脖/轻舟」
  变成「爸爸/妈妈/岳父/岳母/红围脖/轻舟」,即自己消失、后面依次前移;
- 底部说明同步为「共 799 人」;
- 6 个头像逐格取样(圆心 ±25px):色数 223~2162、主色占比 0.00~0.40,
  全部是多色真实图像(纯色首字头会是主色占比>0.75、色数<50)。
hvigorw assembleHap BUILD SUCCESSFUL。
This commit is contained in:
2026-11-06 11:40:26 +08:00
parent f45f4506b0
commit 6a5d351e79
+36 -29
View File
@@ -53,7 +53,7 @@ import { FB_TOKEN_STORE } from '../../data/FbTokenStore';
import { parseTaskMeta } from '../../data/FbParse'; import { parseTaskMeta } from '../../data/FbParse';
import { FB_CARD_FORMS } from '../../data/FbCardForms'; import { FB_CARD_FORMS } from '../../data/FbCardForms';
import { fbApi, FbApiResult } from '../../data/FbHttp'; import { fbApi, FbApiResult } from '../../data/FbHttp';
import { FbSectionTitle } from '../../components/Widgets'; import { FbAvatar, FbSectionTitle } from '../../components/Widgets';
import { FbStatCell } from '../../components/FbStatCell'; import { FbStatCell } from '../../components/FbStatCell';
import { FbDayEventPanel } from '../../components/FbDayEventPanel'; import { FbDayEventPanel } from '../../components/FbDayEventPanel';
import { FbEventSheet } from '../../components/FbEventSheet'; import { FbEventSheet } from '../../components/FbEventSheet';
@@ -1124,10 +1124,25 @@ export struct FbHomePage {
} }
try { try {
const people: FbRosterPerson[] = await FbPersonRepo.inst().list(); const people: FbRosterPerson[] = await FbPersonRepo.inst().list();
this.pfTotal = people.length; /* 🔴 用户 2026-11-06:「首页联系人,需要带头像,**排除自己**。」
⇒ 从名册里剔掉"我"自己。首页这块是"家里人速览",
把自己列在里面既没用(头像是自己)又占掉一个宝贵的位置
(一屏只露 6 个,3 列 × 2 行)。
⚠️ 用 `session.me.id` 判,**不用名字/昵称比** —— 重名、改名都会漏;
名册的 `id` 与服务端 persons.id 同一套,稳。
⚠️ 取不到(0,比如还没登录完)时**不剔任何人**:宁可多显示一个,
也不要因为一个空 id 把整个列表清空(历史上踩过"suspend 条件写反
⇒ 列表恒空"的坑)。
⚠️ `pfTotal` 用**剔除后**的数量:它渲染成标题右侧「N 人」,
若还是原始总数,卡片会写"N 人"却只列出 N-1 个,看着像丢人。 */
const meId: number = this.app.session.me.id;
const others: FbRosterPerson[] = meId > 0
? people.filter((x: FbRosterPerson): boolean => x.id !== meId)
: people;
this.pfTotal = others.length;
/* 卡片上只露前 6 个:与 `HomeCards` 的 `contacts` 默认 `n => 8` 同口径, /* 卡片上只露前 6 个:与 `HomeCards` 的 `contacts` 默认 `n => 8` 同口径,
手机上 6 个(3 列 × 2 行)刚好一屏内不压其他卡片。 */ 手机上 6 个(3 列 × 2 行)刚好一屏内不压其他卡片。 */
this.pfRecent = people.slice(0, 6); this.pfRecent = others.slice(0, 6);
} catch (err) { } catch (err) {
hilog.warn(DOMAIN, TAG, '首页读名册失败:%{public}s', JSON.stringify(err)); hilog.warn(DOMAIN, TAG, '首页读名册失败:%{public}s', JSON.stringify(err));
this.pfRecent = []; this.pfRecent = [];
@@ -2759,27 +2774,22 @@ export struct FbHomePage {
@Builder @Builder
personTile(p: FbRosterPerson) { personTile(p: FbRosterPerson) {
Column({ space: 5 }) { Column({ space: 5 }) {
if (p.avatar !== '') { /* 🔴 用户 2026-11-06:「首页联系人,需要带头像」。
Image(p.avatar) 改前这里是 `if (p.avatar !== '') { Image(p.avatar) }` —— 头像**从来显示不出来**。
.width(38) 根因:`FbRosterPerson.avatar` 存的是**存储键**(`avatars/xxx.jpg`),
.height(38) 不是能直接喂给 `Image()` 的地址。`Image('avatars/xxx.jpg')` 会被当成
.borderRadius(19) 相对沙箱路径 ⇒ 静默失败 ⇒ 永远走不到图,只剩首字色块。
.objectFit(ImageFit.Cover) ⇒ 统一改用 `FbAvatar`:它接受 `imageUrl`(用 `fbImageSrc(key, token)`
.alt($r('app.media.fb_ic_people')) 拼好传进来),并且**底色+首字常驻、图片成功才盖上去** ——
} else { 名册 800 人里只有 19 人传过头像,没图的必须回落到首字,
/* 没有头像 ⇒ 首字 + 名册里那个人的底色(与名册页一致,不另算颜色)。 而"有 key 但图坏了"时也不会变成一块空光板(`Image` 失败是空白)。
⚠️ 字也要用**对色**(`FbPersonRepo` 存了 avatarColor 是给底用的)—— ⚠️ 尺寸 38 与原实现一致(三列网格、两行不压其它卡)。 */
这里简单用白字:底色本身就是中深色(名册页的取色规则)。 */ FbAvatar({
Text(this.personInitial(p)) name: this.personLabel(p),
.width(38) color: p.avatarColor !== '' ? p.avatarColor : '#B7A99A',
.height(38) dia: 38,
.borderRadius(19) imageUrl: p.avatar !== '' ? fbImageSrc(p.avatar, FB_TOKEN_STORE.get()) : ''
.backgroundColor(p.avatarColor !== '' ? p.avatarColor : '#B7A99A') })
.fontColor(Color.White)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.textAlign(TextAlign.Center)
}
Text(this.personLabel(p)) Text(this.personLabel(p))
.fontSize(FbMetric.fontTiny) .fontSize(FbMetric.fontTiny)
@@ -2814,11 +2824,8 @@ export struct FbHomePage {
return p.name; return p.name;
} }
/** 首字(头像兜底)。空名兜底成 `?`,别画出一个空圆。 */ /** 首字兜底已移交给 `FbAvatar`(它内部按 `name` 取首字)。
private personInitial(p: FbRosterPerson): string { * 2026-11-06 首页联系人改用它之后,这里的 `personInitial()` 成了死代码,删掉。 */
const nm: string = this.personLabel(p);
return nm.length > 0 ? nm.substring(0, 1) : '?';
}
/** /**
* 「最近纪念日」里的一行(v59,用户 2026-10-18)。 * 「最近纪念日」里的一行(v59,用户 2026-10-18)。