v0.8.4 简要卡观感 / 分享 / 名册搜索 / 全局返回手势(v59e)

八条需求的落地与真因:

1. 浮层与白底"糊在一起"、看不清 —— 根因不是配色,是**遮罩从来没画出来过**:
   ArkUI 的八位色值是 #AARRGGBB(alpha 在前),而全应用写的是 '#00000066'
   (本意"黑 40%")⇒ alpha = 0x00 ⇒ 全透明。同型还有 brand + '1A'(拼到了蓝通道)
   与 shadow('#00000033')。⇒ 17 个文件统一改走 fbAlpha() / FB_MASK / FB_SHADOW /
   FB_HAIRLINE;真机像素采样复验:遮罩 (148,146,140)、卡片白、浅底 (237,242,243)。
2. 简要卡外圈加**主题色描边环**(外层铺色 + padding,不是 border —— border 会被
   内层圆角切出白缝)。
3. 简要卡**去掉删除按钮**,删除挪到完整详情卡最下面(Divider + 红字块)。
   顺手修一处静默失败:confirmDelete() 原来只取 quickSource(),
   从家谱直接开详情时 quickPerson 为 null ⇒ 点删除毫无反应。
4. 信息行间距 10 → 16(每行再各加 3vp 上下内边距,实测行距 134/118px)。
5. 主电话整行可点**直接拨号**(call.makeCall,落到系统拨号盘)。
6. 按钮改为 详情 / 编辑 / 分享 / 关闭;分享走系统分享面板,
   内容为纯文本「姓名 / 电话 / 邮箱」三项(用户点名的就是这三项,
   不多带单位),面板拉不起来时兜底复制到剪贴板并提示。
7. 名册搜索"完全不能用"有两个独立真因:
   ① 搜索框是 List 的第一个 item,无结果时整个 List 被空态替换 ⇒ 输入框被卸载、
      焦点丢失 ⇒ 输不进第二个字。改:List 无条件渲染,空态自身做成 ListItem。
   ② rowToPerson 把 initials 写死成空串,服务端下发的拼音首字母从未落库 ⇒
      拼音搜索恒 0 结果。改:FB_DB_VERSION 6→7 + PRAGMA 探测补列 + 同步时落库;
      匹配同时遍历全部电话(原来只比主号)。
   另加搜索框「清空」(24×24 命中区)。
8. 全局返回手势:应用是单页 + 模块分发、没有路由栈 ⇒ 自建 FB_BACK 登记表
   (倒序询问)+ moduleHistory + applyModule()(不记历史,否则 A/B 两页来回弹)。
   9 个页面登记/注销;Index.onBackPress 先关浮层 → 回上一模块 → 才 return false
   交给系统退桌面(onBackPress 必须声明 boolean,返回 void 会被当成没接管)。

验证:
- 静态守卫 417 / 0(新增 ⑤阴影·描边·色环 ⑥透明度色 ⑦返回手势 ⑧搜索 ⑨分享)
- 反向验证 10 / 0(9 条注入全部能让守卫变红;其中"遮罩换纯黑"那条暴露了
  一条存在型假绿,已改成数个数 + 禁止 Color.Black)
- 建表/迁移列一致性守卫 12 / 0
- 真机全模块遍历 14 / 14;真机逐项复核 12 / 0 与 7 / 0

工具侧顺带修好两个过期的探针:
- 遍历探针仍用"应用内横滑"打开抽屉,而该手势早已按用户要求去掉、入口改为顶栏图标
  ⇒ 一次全量遍历里 12 个模块全报"抽屉里没找到"(像应用坏了,其实是探针过期)。
- 资源/笔记两页写死了早已不存在的条目(户口本/房产证/阳台改造清单)⇒ 改成
  结构性文案(副标题 + 常驻筛选胶囊),不随用户数据变化而恒红。
This commit is contained in:
2026-10-18 23:12:57 +08:00
parent 1c1a859464
commit 7858de27e3
26 changed files with 1213 additions and 257 deletions
+20
View File
@@ -342,6 +342,26 @@ export class FbDb {
}
}
// ---- v6 → v7:`persons.initials`(拼音首字母)----
//
// 🔴🔴 症状(用户 2026-10-18):「名册的搜索是完全不能用的」。
// 过滤逻辑本身是对的,坏在**数据**:`rowToPerson` 把 `initials` 写死成空串
// ⇒ 「输入 yy 搜杨永全」永远 0 结果(中文名包含匹配还能用,拼音首字母全废)。
// 服务端 `/roster` 一直都在下发 `initials`,只是端上没这一列可存。
//
// ⚠️ 与 v6 同一套写法:先 `PRAGMA` 探测再 ALTER。
// 升上来之后第一次同步(进名册页就会跑)才把值填进去 ——
// 迁移**只加列不给值**,不要试图在这里写死任何东西。
if (from < 7) {
const has: boolean = await this.columnExists(store, 'persons', 'initials');
if (has) {
hilog.info(DOMAIN, TAG, 'v7 迁移:persons.initials 已在(跳过 ALTER)');
} else {
await store.executeSql('ALTER TABLE persons ADD COLUMN initials TEXT');
hilog.info(DOMAIN, TAG, 'v7 迁移完成:补上 persons.initials(拼音搜索)');
}
}
store.version = FB_DB_VERSION;
}
+15 -3
View File
@@ -328,6 +328,16 @@ export class FbPersonRepo {
b['org'] = p.org;
b['title'] = p.title;
b['slogan'] = p.slogan;
/* 🔴🔴 v59e(2026-10-18 真机定位):拼音首字母**必须落库**。
*
* `rowToPerson` 原来把它写死成空串,于是「输入 yy 搜杨永全」这条
* 明确的需求在名册页**从来没生效过** —— 用户的原话是
* 「名册的搜索是完全不能用的」。
* ⚠️ 服务端 `/roster` 本来就下发 `initials`
* (见 ApiHandler 的 `Pinyin::initials(name + nickname)`),端上只是**没存**。
* ⚠️ 只补这一处不够,还要:建表列 + 迁移列 + `list/byId` 的 SELECT +
* `rowToPerson` 的读取(四处缺一,现象都是"搜不到人")。 */
b['initials'] = p.initials;
b['address'] = p.addrCity; // 名册只给到城市
b['addr_city'] = p.addrCity;
b['updated_at'] = '';
@@ -490,7 +500,7 @@ export class FbPersonRepo {
async list(keyword: string = ''): Promise<FbRosterPerson[]> {
const sql: string =
'SELECT p.id, p.name, p.nickname, p.gender, p.birthday, p.avatar, p.avatar_color,' +
' p.note, p.email, p.org, p.title, p.slogan, p.addr_city,' +
' p.note, p.email, p.org, p.title, p.slogan, p.addr_city, p.initials,' +
' r.sort_order, r.phones,' +
' (SELECT GROUP_CONCAT(t.tag, "\\u0001") FROM person_tags t' +
' WHERE t.person_id = p.id AND t.owner_id = r.owner_id) AS tags' +
@@ -528,7 +538,7 @@ export class FbPersonRepo {
}
const sql: string =
'SELECT p.id, p.name, p.nickname, p.gender, p.birthday, p.avatar, p.avatar_color,' +
' p.note, p.email, p.org, p.title, p.slogan, p.addr_city,' +
' p.note, p.email, p.org, p.title, p.slogan, p.addr_city, p.initials,' +
' 0 AS sort_order, NULL AS phones,' +
' (SELECT GROUP_CONCAT(t.tag, "\\u0001") FROM person_tags t' +
' WHERE t.person_id = p.id) AS tags' +
@@ -665,7 +675,9 @@ export class FbPersonRepo {
// (只有走接口的 `listWithLabel()` 才带 phone)。这是个真 bug。
phone: phonesArr.length > 0 ? phonesArr[0] : '',
phones: phonesArr,
initials: '',
/* 🔴 v59e:**从库里读**(原来写死成空串 ⇒ 拼音首字母搜索全废)。
⚠️ 见 `sync()` 里 `b['initials']` 那段说明:这一项是四处一起改才通的。 */
initials: r.strOr('initials', ''),
org: r.strOr('org', ''),
title: r.strOr('title', ''),
slogan: r.strOr('slogan', ''),
+9 -1
View File
@@ -37,7 +37,7 @@ export const FB_DB_NAME: string = 'familybook.db';
* 本地库**不能像服务端那样"每次启动都全量 ALTER 一遍"** ——
* 那是拿用户的手机当试验田。必须按版本逐步迁移。
*/
export const FB_DB_VERSION: number = 6;
export const FB_DB_VERSION: number = 7;
/**
* 建表语句。
@@ -141,6 +141,14 @@ export const FB_SCHEMA_SQL: string[] = [
slogan TEXT,
address TEXT,
addr_city TEXT,
-- 🔴 v7(v59e)新增:拼音首字母(服务端 Pinyin::initials 算好下发)。
-- 名册搜索「输入 yy 搜杨永全」全靠它;原来端上把它写死成空串 ⇒ 拼音搜索全废。
-- ⚠️ 加列时**两处都要改**:这里(全新安装)+ FbDb.migrate() 的 v6→v7
-- (老库升级)。只改一处就是"新装能搜、老库搜不到"或者反过来。
-- ⚠️ 本段是 SQL 注释、而整条建表语句是 JS 模板字符串 —— 注释里**绝不能
-- 出现反引号**(一个反引号就会把模板串提前闭合,报一堆
-- "Cannot find name Pinyin/FbDb" 这种看着毫不相干的错)。已踩第 5 次。
initials TEXT,
created_at TEXT,
updated_at TEXT NOT NULL DEFAULT '',
deleted_at TEXT,