v0.8.3 头像显示 / 主题色标签 / 右侧贴边 / 简要卡片 / 浮层分离

用户 2026-10-18 第二批五条:

①「右上角的个人头像没显示。」
   🔴 真根因:`/me` 与 `/login` 返回的 `avatar_url` 是**相对地址**
      (`media.php?k=avatars%2Fxxx.jpg` —— 不带域名、不带令牌),
      那是给**浏览器**用的(同源 + cookie);`Image()` 要完整 URL ⇒ 加载失败。
      真机日志:`GetAsset failed: media.php?k=avatars%2F20261002_...jpg`
   ⇒ 新增 `fbMediaUrl(raw, token)` 归一化,**三种形态都认**
      (绝对地址 / media.php 相对式 / 裸 key)—— 少认一种就会出现
      "有的头像能显示、有的不能",那类 bug 最难查。
   ⚠️ 不能复用 `fbImageSrc()`:那是"附件 key → 本地缓存"的通道,
      把整条 URL 当 key 传进去会得到一个必然 404 的地址。

②「名册、家谱这两个标签,用的不是主题色。」
   🔴 两个调用点**各硬编码 `'#A84A28'`**(品牌砖红),而名册模块主题色是
      `#4A7C82`(青)⇒ 与整页不在一个色系。
   ⇒ `segBtn` 不再由调用方传色,内部取 `fbThemeVars(fbModuleOf('people').color).brand`;
      顺手消掉"两处各写一遍字面量"这个漂移源。

③「名册列表,左侧有空隙了……怎么右侧不是呢?右侧还是紧贴屏幕。」
   🔴 实测(像素扫描):左米色 50~72px、**右米色 0px**。
      根因是 `Row` 内 `layoutWeight(1)` 的子项默认 `min-width: auto`
      (按内容最小宽度)⇒ 长名字/多标签把**卡片布局盒撑破** ⇒ 右边越界贴屏幕
      (左边先被 margin 占位、所以还留着空隙)。
   ⇒ 弹性子项开 `flexShrink(1)`,姓名那行连同 `width('100%')`。

④「点击列表,不打电话了,容易误触。而是弹出一个卡片,卡片中是头像、名称、
   电话号码、邮箱等简要信息,下面三个按钮,详情、编辑和删除。……
   列表页面中,也不要详情按钮了。」
   ⇒ 新建 `FbPersonQuickSheet`(@ComponentV2):
      头像 / 姓名+称谓 / 电话(主号 + 其他)/ 邮箱 / 单位 / 生日
      + 三个等宽按钮「详情 / 编辑 / 删除」。
   ⇒ 点整行走 `openQuick()`(**不再拨号**);列表里的「详情」按钮**删除**。
   ⇒ 删除走 `AlertDialog` **二次确认**(服务端物理删除、无回收站 ⇒ 误触不可逆),
      文案带姓名("确定把「老杨」…")—— 误触时最需要"是不是点错了人"这个信息。
   ⚠️ 两级浮层分开两个状态(`quickPerson` / `detailPerson`);详情关掉**回到简要卡**,
      而不是一路退回列表。

⑤「这个卡片,弹出来之后,是白色的,和下面的列表中的白色,都融合在一起,
   根本看不出是弹框。」
   🔴 原来只靠 `#00000066` 遮罩区分 —— 而本应用**列表本身就是大面积白底**,
      白卡压在"被压暗的白"上边界仍发飘。
   ⇒ **三层分离**:① 遮罩(宿主)② 卡片**阴影**(抬起来)③ 极浅**描边**
      (同色背景上仍有一条可见界线;只靠阴影在浅底上不够稳)。
   ⚠️ 三个浮层(简要卡 / 详情 / 编辑)**同手段** —— 否则从简要卡切到编辑会突然"变平"。

验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 377/0(新增 28 条,
另修 3 条旧判据);_fb_v59c_falsify.py 16 项注入 ⇒ 16/0。

⚠️ 本轮反向验证又抓出 2 条假绿 + 1 条"没真注入",已逐条收紧(记入 pitfalls):
  · `flexShrink(1)` 在本页有**两处**(姓名 Text / 整个 Column)⇒ "出现过"型判据
    删掉关键那处仍绿;改成锚"相邻关系"(`.layoutWeight(1)` 之后 220 字符内);
  · `showAlertDialog` 被 `if (false)` 包起来(确认框永不弹)⇒ 字符串还在、判据仍绿;
    改成查因果(窗口内有 `value: '删除'` + `this.doDelete(who)`);
  · 有一条注入只改了注释、没删那行代码 ⇒ 报 [MISS],那是"注入没打到点上"。
This commit is contained in:
2026-10-18 21:47:18 +08:00
parent 9bf21cd853
commit 412d2ae2f7
6 changed files with 653 additions and 46 deletions
+47
View File
@@ -45,6 +45,53 @@ export function fbApiBase(): string {
return FB_SERVER_STORE.get();
}
/**
* 把**服务端给的媒体地址**变成 `Image()` 能直接加载的完整 URL。
*
* 🔴🔴 为什么需要它(2026-10-18 真机 bug:右上角头像不显示):
* 服务端 `avatar_url()` / `media_url()` 返回的是**相对地址**
* (`media.php?k=avatars%2Fxxx.jpg` —— 不带域名、不带令牌),
* 那是给**浏览器**用的(同源、cookie 自动带上)。
* 而 `Image()` 需要一个**完整可访问**的 URL ⇒ 拿到相对地址直接加载失败。
* 真机日志就是这么写的:
* `GetAsset failed: media.php?k=avatars%2F20261002_e32c8647d2f105ce.jpg`
*
* ⚠️ 三种输入都要认(服务端不同接口给的形态不一样):
* ① 已经带 `http(s)://` ⇒ 原样返回(`avatar_url` 对历史外链数据就是这么处理的);
* ② 形如 `media.php?k=<key>` ⇒ 补域名(**保留它自带的 k**,别再 encode 一次,
* 否则 key 会被二次编码成 `avatars%252F...`,服务端解出来是错的路径);
* ③ 裸 key(`avatars/xxx.jpg`)⇒ 走 `fbImageUrl()` 按标准形式拼。
* ⚠️ 传空串给回空串 —— `FbAvatar` 以此判断"回落色块字",**不要**给它拼一个默认图,
* 否则所有没头像的人都会去请求一张不存在的图。
*
* @param raw 服务端给的地址(相对/绝对/裸 key)
* @param token API 令牌(追加到 query,媒体代理要靠它鉴权)
*/
export function fbMediaUrl(raw: string, token: string): string {
const s: string = raw.trim();
if (s === '') {
return '';
}
// ① 绝对地址原样用
if (s.indexOf('http://') === 0 || s.indexOf('https://') === 0) {
return s;
}
const base: string = fbApiBase();
if (base === '') {
return '';
}
// ② 已经是 media.php 形式 ⇒ 只补域名(k 已经编码过了,别动它)
if (s.indexOf('media.php') === 0 || s.indexOf('/media.php') === 0) {
let url: string = base + '/' + (s.charAt(0) === '/' ? s.substring(1) : s);
if (token !== '') {
url += '&token=' + encodeURIComponent(token);
}
return url;
}
// ③ 裸 key ⇒ 标准拼法(走 media.php?k=...)
return fbImageUrl(s, token);
}
/**
* 拼一个可直接给 `Image()` 用的图片地址。
*