laoyang
|
d9ca9dbe31
|
下拉框与网页端对齐(照 FeedLite 那套观感)(#86)
用户 2026-11-05:「好了,鸿蒙端也用类似的方式调,效果让它保持一致。」
端上**不需要像网页那样手写浮层** —— 网页端必须自绘是因为
「原生 select 的展开列表由浏览器绘制,CSS 改不动」;
ArkUI 的 Select 本身就是自绘的、选项列表也归框架管。
⇒ 把对应属性都设上就等价了:
网页端 ArkUI Select
──────────────────────────────────────────────────────
按钮 40 高 / 圆角 / 细边框 height(40) + borderRadius + border
箭头打开转 180° 框架内置(自带动画)
悬停 tint-1 不支持(触屏没有 hover)→ 用打开态代替
选中项底 tint-2 selectedOptionBgColor(fbMixWhite(accent,0.80))
选中项文字 主题色 selectedOptionFontColor(accent)
选中项右侧对勾 showDefaultSelectedIcon(true)
浮层白底 + 细边框 menuBackgroundColor(White) + border
浮层与按钮同宽 optionWidth(OptionWidthMode.FIT_TRIGGER)
未选值次要色 fontColor(有值 ? accent : Faint)
① Theme 新增 fbMixWhite(hex, ratio)
与网页端 fb_color_tint() **同算法同比例**(混 91/80/72% 白)。
⚠️ 为什么不用现成的 fbAlpha():那是「原色 + 透明度」,
会透出父容器底色 —— 下拉浮层背景不确定,那样会时深时浅;
这里是实色混合,颜色确定。
⚠️ 实测 16 组(4 个主题色 × 4 档比例)与 PHP 输出逐个比对,全部一致。
② 边框单独用一档
按钮边框 fbMixWhite(accent, 0.62) —— 比 tint-3 再深一点,
否则在 tint-1 的浅底上几乎看不见边界。
③ 修一个类型坑
本来写了个 pickFontColor(): string 返回 accent / FbColors.muted,
但 FbColors.muted 是 Resource($r(...)),返回 string | Resource
会让 fontColor() 报「Type 'string | Resource' is not assignable to 'string'」。
⇒ 改成内联三元,且未选用 FbColors.faint(那个是 string 常量)。
编译通过,已装真机。
|
2026-11-05 20:25:59 +08:00 |
|
laoyang
|
389cb31df4
|
分类下拉:修「永远未分类」+ 菜单同宽 + 主题色 (#84)
用户 2026-11-05 三点:
1.「分类那个地方没带进来。明明是有分类的,但是它却显示的是未分类。」
2.「分类弹出来的框太窄了,应该和输入框一样宽。」
3.「颜色也不对…文字颜色是主题色,背景色是比主题色浅一点的那个主题色。」
① tskCats 永远是空数组
`FbHomePage.loadTaskMeta()` 开头:
if (this.tskBoards.length > 0) { return; }
**只看看板有没有值**。这个方法跑过一次、看板填上之后,
以后再进来直接返回 ⇒ `tskCats` 永远停在上一次的赋值上;
如果第一次调用时 cats 恰好是空(或 #83 之前的版本压根没赋过),
就再也补不上 ⇒ 下拉里只有「未分类」。
⇒ 改成 `if (boards.length > 0 && cats.length > 0) return;`
"两样都有值才跳过"。
⚠️ 不能只判 cats:新家庭可能真的一条分类都没建,那样每次都重拉。
⚠️ 任务页的 `loadMeta()` 没有这个提前返回,不用改。
② 下拉菜单太窄
`width(100%)` 管的是**控件本身**;弹出的选项菜单有独立宽度
(`optionWidth`),不给就按选项文字撑开 ⇒ 很窄。
⇒ `.optionWidth(OptionWidthMode.FIT_TRIGGER)` 让它跟触发器同宽。
⚠️ 顺手试过 `.optionHeight(auto)` —— SDK 里 `optionHeight` 只收
`Dimension`,传 auto 直接编译报 "No overload matches this call"。
③ 颜色改主题色
文字 `this.accent` + `weight: Medium`;
底色 `fbAlpha(this.accent, 0.10)`(浅一档的主题色)。
与面板里「+ 添加附件」那颗胶囊同一套 —— 整面板"可操作控件"观感一致。
选项列表里:选中项用主题色、其余用墨色(全主题色会失去对比)。
编译通过,已装真机。
|
2026-11-05 20:19:43 +08:00 |
|
laoyang
|
79bc593d43
|
鸿蒙端:分类、循环改成可编辑 (#83)
用户 2026-11-05:「分类和重复,也就是循环,都需要加上,鸿蒙端也得加上。」
原来这两个只在**有值**时才画一行只读文字
(`if (this.categoryOf() !== )` / `if (this.repeatOf() !== )`)——
用户不仅改不了,连"这里本来有这一项"都看不出来。
(这与 #76 里网页端"负责人为空就整行消失"是同一个毛病。)
改法:与截止/负责人同形态(标签一行 + 控件一行),**始终显示**。
① FbTaskPanel 新增 `@Param cats: FbCatOpt[]`
⚠️ 与 boards 同一理由:**不能从任务里反推** ——
反推只能看见"已经被用过的"分类,新建的分类在端上永远选不到。
② 下拉用 ArkUI 原生 `Select`,第 0 项固定「未分类」/「不循环」
- 分类:index 0 → category_id = 0;index N → cats[N-1].id
- 循环:index 0 → (不循环);index N → REPEAT_KEYS[N-1]
⚠️ REPEAT_KEYS 与 REPEAT_WORDS **必须严格对齐**,
错一位就是"选每天存成每周"这种静默错数据。
③ `inlineBusy` 时退回只读文字:请求在飞的时候不给选,
否则用户连点两下会发两次相反的值。
④ 首页 / 任务页两处宿主传 `cats`(tskCats / metaCats)。
⑤ 服务端 API 早就支持这两个字段(`Api::str(repeat_rule, ...)`),
本轮只补了 `Task::setAttrs` 的 repeat_rule 分支(见服务端那次提交)。
编译通过,已装真机。
|
2026-11-05 20:15:25 +08:00 |
|
laoyang
|
8fe1131334
|
去掉截止、负责人右侧的小铅笔图标 (#77)
用户 2026-11-05:「截止日期和负责人右侧都有一个编辑用的、像小铅笔似的图标。
我感觉这个东西不需要了。如果要加的话,全部都得加上,台式机采购计划、测试
之类的都要加上。但现在我觉得不加也可以,就把所有小图标全部去掉,
也就是右侧那个小铅笔图标全去掉。」
删掉两处 `✎`:
· 截止那行(rows: 到期时间 + 已逾期徽标 + ✎)
· 负责人那行(头像 + 名字 + ✎)
上一版加它是想提示"这里能点"(本项目踩过"纯文字看着不可点"的坑),
但代价是:
① **只有这两行有、别的行没有** ⇒ 视觉上不齐,看着像漏做;
② 输入框本身(浅底圆角 40 高)已经足够表达"这是个可操作的控件",
铅笔是重复提示。
⚠️ 全项目扫过:`✎` 只在 FbTaskPanel 这两处,已全部清零。
⚠️ 注释里记下取舍:以后若要恢复"可点"提示,必须**所有行一起加**
(任务标题/截止/负责人/分类/循环/备注),不能只给其中两行 ——
那才是用户不接受的地方,不是"有提示"本身。
编译通过,已装真机。
|
2026-11-05 20:04:59 +08:00 |
|
laoyang
|
619f800ae7
|
删除按钮从底栏移到内容最底部(危险操作区)(#77)
用户 2026-11-05:「删除按钮不能出现在那里,因为这样太容易误触了。
删除按钮应该放在哪里?你考虑考虑。」
放在**滚动内容的最末尾**。
为什么不在底栏(原来和「保存」并排):
① **误触**:底栏是"保存"这种高频操作的位置,手一滑就点到删除,
而删除**不可恢复**,一次误触 = 数据没了。
② **语义错位**:底栏是"我改完了、要留下这个结果"的确认区,
不该混进"我要毁掉它"。
为什么不放顶栏:顶栏是"知道我在哪 / 怎么退出"的位置,
在那里放删除,误触代价同样不可恢复(还紧挨着关闭按钮)。
放最底部的好处:
· 用户得主动滚到最底才看得见 —— 低频操作不必首屏可见;
· 纯文字(fontTiny)、无填充底色,**不像按钮**,不招手指;
· 与正文隔一条分割线,视觉上属于"其它",不属于"操作";
· 这是通行做法(Notion / 备忘录 / 各类设置页的删除都在最底部)。
二次确认也重做了:
第一下「删除这个任务」→
变成「删了就找不回来了,确定吗?」+ [确定删掉] [取消]
原来只有"再点一次"、没有取消出口,误点后只能点别处退出
(不明显)。现在有明确的「取消」可退。
底栏现在只剩**全宽的「保存」**,视觉上更明确:这一条就是"确认改动"。
编译通过,已装真机。
|
2026-11-05 20:03:05 +08:00 |
|
laoyang
|
ad63b4c89d
|
修:面板底部「保存/删除」按钮被裁掉看不见 (#77)
用户 2026-11-05:「你改的是什么?是鸿蒙版吗?怎么没有看到任何按钮啊?」
代码确实在(我解包 sy-77.hap 在 modules.abc 里搜到了"保存中"字符串),
是**布局把它裁掉了**。
真根因:中间那个 Scroll 用的是 `flexShrink(1)` ——
`flexShrink` 只表示"空间不够时允许收缩",但 Scroll 的**内容高度**
仍会把自身撑开(ArkUI 竖向 Scroll 默认按内容高参与布局)。于是:
头部 + Scroll(按内容撑满) 吃光 `maxHeight: 86%`
→ 下面的 footBar 被推到 86% 之外
→ `FbSheetRing.clip(true)` 把它整条裁掉
⇒ 一颗按钮都看不见(而且连痕迹都没有,看起来就像"没做")。
改法:
① Scroll → `layoutWeight(1)`
"占据剩余空间"而不是"按内容撑开"。高度被限定成
"86% − 头部 − 分割线 − footBar",footBar 因此必然留在可视区内,
内容再长也只在自己内部滚动。
② footBar 补 `flexShrink(0)`
显式禁止压缩,防止某些机型把 42 高的按钮压扁。
⚠️ 旧注释(v60b)说 `layoutWeight(1)` 会让"短内容也把卡片顶满、底下留一片空白"。
那个副作用是真的,但属于观感问题;按钮看不见是功能问题。
两害相权先保证按钮可用。要消空白的话正确做法是外层配 minHeight:0,
而不是回到 flexShrink(那条路会让底栏被挤出去)。
编译通过,已装真机。
|
2026-11-05 19:58:53 +08:00 |
|
laoyang
|
980630644b
|
底部「编辑」换成「保存」(#77)
用户 2026-11-05:「和网页版一样,没有保存按钮啊,这应该怎么保存呢?
在哪个地方点保存呢?」
之前是"失焦自动保存"——逻辑上能存,但**用户看不见这件事**,
没有按钮就没底,不知道到底存没存。补一颗显式按钮。
① footBar 的「编辑」→「保存」
原来那颗「编辑」本身就是矛盾的:面板已经能改所有字段
(标题/备注/截止/负责人/分类/协作人/子任务/附件/留言),
再给一颗"编辑"反而跳到另一张表单。
⇒ 换成「保存」,保存后关窗(与网页端保存后关浮层一致)。
② saveAll():串行存标题 + 备注,然后 onChanged() + onClose()
⚠️ 必须**串行**不能并发:`saveField()` 内部会 `reload()`,
两次并发会让后一次的 reload 覆盖前一次的结果,
表现为"改完标题,备注却回到旧值"。
⚠️ 有 `saving` 忙碌态:避免连点两次发四组请求。
③ 其余字段保持"点了就生效",不按保存
状态/看板/截止/负责人/分类/协作人/子任务/附件/留言 都是**动作**
(选中即生效),多一步确认反而烦;只有"打字"类(标题、备注)
才需要一次确认 —— 这也是网页端的口径。
编译通过,已装真机。
|
2026-11-05 19:54:30 +08:00 |
|
laoyang
|
cbeeb55a82
|
任务标题改用单行输入框 (#77)
用户 2026-11-05:「现在这个任务标题是好几行的那种输入框,不要用这种输入框,
用一行的输入框就行了。标题没有那么长,现在太高了。」
TextArea(多行,height 84)→ TextInput(单行,height 40)。
① 高度 84 → 40,与「截止」「负责人」那些控件行同高
标题是一句话,本来不需要多行;84 的高度等于多占两行位置,
把下面所有内容(状态/看板/截止…)整体往下推。
② 顺带补 onSubmit:单行框里回车是"提交"而不是换行,
用户打完直接按回车就保存,不用先点别处。
多行框里回车是换行,所以以前不能加这一条。
⚠️ TextInput 只有一行,高度超了也只是上下留白 ⇒ 直接给 40,
不是像 TextArea 那样"给多少显示多少"。
⚠️ 单行框超长时不换行、横向滚动 —— 标题本来就没那么长,可接受。
编译通过,已装真机。
|
2026-11-05 19:52:23 +08:00 |
|
laoyang
|
10cd10e9ff
|
顶栏去掉重复状态徽标 + 标题输入框与其它统一 (#77)
用户 2026-11-05 两点:
1.「编辑任务那一行,右上角还有个"进行中",那个也去掉吧,
因为在状态那里已经看到"进行中"了。」
2.「台式机采购计划这个任务标题的输入框和其他的都不一样。我感觉没必要,
字体什么的大小,和下面的备注还有什么的保持一致就行了,没必要特别特殊。」
① 删掉顶栏的状态徽标
下面「状态」区三个 chip 已经把当前态高亮出来了,顶栏再挂一个只读徽标
是同一信息画两遍。顶栏现在只剩「编辑任务/属于父任务 + 关闭」。
⚠️ `statusWord() / statusTint() / statusWash()` 三个方法**保留** ——
状态区的 statusChip 还在用。
⚠️ 上一版注释写"只留状态徽标 + 关闭",与现实不符了,已同步改掉。
② 标题输入框与备注完全统一
改前:fontSize(fontTitle=17) + Bold + height(64)
改后:fontSize(fontSmall=13) + 不加粗 + height(84)
三项参数与备注(noteDraft 那个 TextArea)**逐项一致**。
标题的"重要性"由它排在最前 + 标签主题色表达,输入区不必再靠字号放大。
编译通过,已装真机。
|
2026-11-05 19:50:45 +08:00 |
|
laoyang
|
d4b9d08b7e
|
标题/备注改成常驻输入框 + 内容用墨黑 (#77)
用户 2026-11-05 两点:
1.「任务标题里面,"台式机采购计划"这样的字样还是改成黑色吧。
任务标题这四个字用主题色,但是输入框里面的文字用黑色。」
2.「这个输入框本来就应该是输入框,怎么现在点了一下才变成输入框呢?
它应该直接就是输入框,只是把文字放到输入框里面就行了。
这样的话,点一下键盘就弹出来了,包括下面的备注也是一样。」
① 颜色:标签主题色、内容墨黑
上一版我把 TextArea 的 fontColor 也设成 accent,整段标题变成红的,
像报错。现在:
· 「任务标题」四个字 → fieldLb(主题色 + Bold)
· 框里的文字 → FbColors.ink
备注同理。
② 常驻输入框(这是关键的结构修正)
上一版是"只读文字 + 点一下切成 TextArea"的双态(titleEdit / noteEdit),
所以用户看到的是"点一下才变成输入框"。
现在**直接就是 TextArea**,值从 titleDraft / noteDraft 来。
点输入框由系统自己聚焦 + 弹键盘,不需要 requestFocus。
⚠️ 必须是**受控**的:`TextArea({ text: this.titleOf() })` 是非受控,
每次 reload() 重建组件会丢掉用户正在打的字。
⚠️ 删掉 titleEdit / noteEdit 两个状态(双态不存在了),
保存判据改成"值变了没有"。
⚠️ 标题**空值回填、不提交**:服务端会把空标题清洗成空串 = 把标题删了。
用户只是清空重打,不该丢标题。
⚠️ 备注**允许清空**:删掉备注是正常操作,所以 note 不像 title 那样回填。
③ 焦点保护(新增 titleFocus / noteFocus)
`@Monitor(refreshTick)` 会在外部刷新时调 reload(),而 reload 会用
服务端的值覆盖 titleDraft —— 用户正打字时被覆盖就是"打着打着字没了"。
⇒ 有焦点时不覆盖,失焦后再同步(onBlur → save → reload)。
编译通过,已装真机。
|
2026-11-05 19:48:49 +08:00 |
|
laoyang
|
4cca4c51be
|
任务面板:左上「编辑任务」+ 点一下即编辑 + 标题最前 + 字号统一 (#77)
用户 2026-11-05 五点:
「这个框的标题在左上角,是"编辑任务"几个字,颜色用主题颜色。
你参考一下"编辑行程"的做法。」
「任务和备注是点进去之后,只要点一下,马上就要变成编辑状态,
然后弹出输入法,开始输入。不需要点一下之后先变成一个输入框,
再点一下弹出输入法。直接点进去之后就是输入状态。」
「任务标题应该放在最前面,放在所有内容之前,也就是状态之前。」
「状态、分组、看板、任务标题、截止、负责人、备注、协作人、
子任务、附件、留言这些字的大小要保持统一。」
① 左上角「编辑任务」(主题色)
与 FbTripNodeSheet 的「编辑行程」完全同一套:
fontSize(fontBody=15) + Bold + accent。
只写固定的「编辑任务」,不写任务标题 —— 标题是内容(可改),
已经下移到 titleRow()。
「属于「父任务」」跟着放到标题下面第二行(原来是同一行的大字位置)。
② 点一下直接进编辑态并拉起输入法
原来点一下只是 `xxxEdit = true`,把 TextArea 画出来 ——
但**画出输入框不会自动获得焦点**,用户还得再点一下才弹输入法,
正是他说的"变成输入框,再点一下弹出输入法"。
⇒ 点击时:
this.titleEdit = true;
focusControl.requestFocus(tkpTitleInput);
并给控件加 `.id(...)` + `.defaultFocus(true)`。
⚠️ `focusControl` 是**全局声明的命名空间**(ets/component/common.d.ts),
**不能 import** —— 写成 `import { focusControl } from @kit.ArkUI`
会报 "has no exported member"。这个坑踩过一次。
⚠️ `defaultFocus(true)` 是兜底:个别机型上 requestFocus 会因
"节点刚创建"而落空,defaultFocus 能让它自己拿到。
备注同改(id = tkpNoteInput)。
③ 任务标题提到最前
build() 的 Scroll 里顺序改为:
titleRow() → statusSeg() → boardChips() → infoRows() → …
原来它在 infoRows 内部第一个(等于排在状态/看板之后)。
④ 字号统一
`fieldLb` 从 fontTiny(11) 改成 **fontSmall(13)**,
与 `FbSectionTitle` 内部字号完全一致(那个组件是 fontSmall)。
上一版标签比区块标题小一号,看着不成套。
现在统计:状态/分组(看板)/协作人/子任务/附件/留言 六个 FbSectionTitle
全部 fontSmall,任务标题/截止/负责人/备注/分类/循环 六个 fieldLb
也全部 fontSmall ⇒ 11 处标题字号统一。
编译通过,已装真机。
|
2026-11-05 19:45:17 +08:00 |
|
laoyang
|
28c6e674ba
|
任务面板:去掉左上圆圈、标题下移成可编辑的「任务标题」(#77)
用户 2026-11-05:「还是不对,这个弹出框的标题是编辑任务,台式机采购这样的
东西,它是一个标题,也应该可以被修改才对。所以它应该往下放,就是任务标题;
然后台式机采购计划也往下放,也是可以修改的。左上角那个标志,好像一个圆圈
什么的,那个也不要了,去掉。」
① 删掉左上角的 FbCheckCircle("圆圈")
它是"点一下勾完成"的快捷入口,但下面「状态」区已经有三个明确的 chip,
入口重复。而且它紧贴标题左侧,视觉上像"标题前面的项目符号",
反而让人以为标题前面该有个图标。
② 大标题从顶栏下移到「更多信息」区,成为第一项「任务标题」
原来标题画在 headBar 里,所以它是**只读**的 —— 而标题本来就该能改。
现在与截止/负责人/备注同形态:标题行 + 控件行,
点一下变成 TextArea(height 64、字号 fontTitle 17、主题色),
失焦自动保存(与备注同一套交互,不设保存按钮)。
⚠️ 空标题不发请求:服务端会清洗成空串,等于把标题删了。
⚠️ `saveField(title, ...)` 走的是既有 PATCH —— 服务端 ApiHandler
对未提交字段回退原值,所以只传 title 不会把 note/due_at 抹掉。
③ headBar 只剩「状态徽标 + 关闭」,左端放「属于「父任务」」
父任务那行保留在顶栏不跟着下移:它是上下文不是内容,
跟标题走反而离"父任务"这个概念更远。
没有父任务时用 Blank() 占位,保证徽标和关闭仍在右边。
④ 完成态标题仍是 faint + 删除线(不上主题色)
删除线是"这件事已经过去了"的信号,再上主题色会和待办标题一样抢眼。
编译通过,已装真机。
|
2026-11-05 19:41:14 +08:00 |
|
laoyang
|
2fefacccd3
|
任务面板字段改上下两行 + 标题主题色 (#77)
用户 2026-11-05:「还是不对,你还是没有看清楚那个样式的设置:上面的标题和
下面的输入框,其实要分成两行。比如说,"截止"不是直接后面排一个日期,
而是把日期输入框放在"截止"下面;"负责人"也是,"备注"也是,全部都是这样的。
上面"台式机采购计划"这样的标题,应该是主题的颜色。」
前两轮(#77i / #77j)我都在改**颜色和字重**,方向偏了 ——
用户要的是**布局**:标签一行、控件另起一行。
① fieldLb 去掉 width(48)
上一版把它定成 48vp 固定宽的行内标签,形态是「[截止] 2026-11-07 18:30」,
与要求的上下两行正好相反。
现在 width(100%) + textAlign Start,独占一行;
字号从 fontSmall(13) 降到 fontTiny(11):上下两行之后标签再和控件同号
会抢视觉重心。
② infoRows 重写为 Column({space:14}),每项 = Column({space:6}){
标题行 fieldLb(...)
控件行(height 40 / inputBg / radiusS / 左右 padding 12)
}
控件行统一给 height(40):不给的话「设置…」「未指定」这类短文字
会把框压扁,几行之间的间距就参差不齐。
负责人那行的头像从 22 调到 24(框内 40 高,22 略嫌小)。
备注:只读态改 constraintSize({minHeight:40}) + maxLines(3),
避免长备注把框撑成一大片;编辑态从单行 TextInput 换成
TextArea(height 76)—— 备注本来就是多行的,单行输入框里
看不见换行,用户会以为只能写一句。
③ kvRow 同步改成上下两行
「分类」「循环」若还左右排,夹在截止/负责人/备注中间会显得错位。
④ 面板标题改主题色
Text(this.titleOf()) 从 FbColors.ink + Medium 改成 this.accent + Bold。
⚠️ 完成态仍保留 faint + 删除线:那是"这件事已经过去了"的信号,
再上主题色会和待办标题一样抢眼,反而分不出轻重。
编译通过,已装真机。
|
2026-11-05 19:37:08 +08:00 |
|
laoyang
|
b42e657bb2
|
标题颜色/看板/截止/负责人/备注/协作人/留言头像 全部修正 (#77)
用户 2026-11-05:「还是不对,标题的颜色不对。分组看板那个地方没改,截止没改,
负责人没改,备注没改,协作人没改。评论那个地方的头像怎么又没了?」
逐条查下来,四个真问题:
① 上一轮只改了 3 个标题,漏了一大片
只换了「子任务/附件/留言」,而 **看板、状态、协作人**、
以及「更多信息」里的**字段标签**全是手写的 fontTiny + faint。
⇒ 看板/状态/协作人 补 FbSectionTitle(Bold + accent + 字距 0.6)。
⇒ 截止/负责人/备注/分类/循环 这些**行内字段标签**新增 fieldLb() Builder:
同样取主题色 + Bold + letterSpacing,但宽度 48、无内边距
(不能直接用 FbSectionTitle:它 width 100% + padding,会把右侧控件顶下去)。
⚠️ 这与「编辑表单」的口径不同:那边 `期限` 仍是浅灰小字。
用户明确要求这个面板里字段标签也用主题色粗体 —— 按用户要求走,
已在 fieldLb 的注释里写明这个差异,避免以后有人"统一"回去。
② 留言头像丢失的真因
不是数据问题,是 `FbPersonChip` **不透传 imageUrl**。
它内部的 `FbAvatar({ name, color, dia, fontSizeVp })` 漏了这个参数,
而 `FbAvatar` 自己是支持的(注释里还写着"800 人里只有 19 人有头像,
有图显示图、没图回落色块字")。
⇒ 上一轮我换成 FbPersonChip 后,原来手写 FbAvatar 时**能显示的头像反而没了**。
给 FbPersonChip 补 `@Param imageUrl` 并传给内部 FbAvatar,
调用点把 `fbImageSrc(c.avatar, token)` 传进去。
③ 状态区原来没有标题
三个 chip 直接飘在那(上下每个区段都有标题)。补 FbSectionTitle(状态)。
④ 协作人标题改组件
顺手把「+ 共享」按钮挪到标题下面单独一行(FbSectionTitle 是 100% 宽,
放同一个 Row 里会把它压成 0 宽)。
编译通过,已装真机。
|
2026-11-05 19:34:12 +08:00 |
|
laoyang
|
09f7878994
|
样式改为走规范组件 FbSectionTitle / FbPersonChip (#77)
用户 2026-11-05:「样式还没变,还记得咱们之前规定的样式是怎么做的吗?」
找到了。规定就在 entry/src/main/ets/components/Widgets.ets,
而且 FbSectionTitle 的注释里**留着用户原话**:
「"今天的日程",既然是日程,标题颜色就应该用日程的主题色。
…下面的"近期任务"改法跟上面一样,还有"最近纪念日"也不说了,全都是一样的。」
上一轮我做错的地方:**手写 Text 改 fontColor**,没走组件。
而 FbSectionTitle 给的是三样东西:
· fontWeight: accent 存在时 Bold(不是 Medium)
· fontColor: accent
· letterSpacing: 0.6
我只改了颜色 ⇒ 字重和字距还是老样子 ⇒ 用户看着"样式没变"。
改动:
① 子任务 / 附件 / 留言 三个区块标题 → 全部换成 FbSectionTitle
传 accent: this.accent(模块主题色)+ extra(数量/进度)
② 留言人 → 换成 FbPersonChip
网页端留言人用的就是 person_chip()(头像 + 名字的胶囊),
鸿蒙端对应 FbPersonChip。上一轮我手写的"头像 + 纯文字名字"只是长得像,
不是同一套。换掉后同时删掉外置的那个 FbAvatar(组件内置了,会画两遍)。
🔴 踩到一个组件约束:FbSectionTitle 是 `width(100%)` 的独立组件,
塞进 `Row` 里会和后面的计数/按钮抢宽度,被压成 0 宽。
⇒ 三处都改成**独占一行**;「已完成 2/5」这类附加信息走它自带的 extra;
按钮(+ 添加附件)另起一行右对齐。
编译通过,已装真机。
|
2026-11-05 19:29:39 +08:00 |
|
laoyang
|
32a35ca222
|
留言底部被裁 + 留言支持附件 (#77)
用户 2026-11-05:
「留言输入框下面还有东西,现在被挡住了,根本看不见,也滚不动了。
但下面应该还有字什么的,看看是什么。」
「留言是可以需要添加附件的。」
① 被挡住的是什么
是 #77 刚加的「回车发送」那行小字。
根因:`commentSection()` 是 `Scroll` 里**最后一个**子元素,
而 Scroll 内容末尾只留了 `height(6)`。
Scroll 本身是 `flexShrink(1)`(内容不够高时按内容收缩),
最后一行下边距一旦小于"卡片 padding + footBar 高度",
就会被判定为"已到底" —— 滚不过去、也看不见。
⇒ 底部留白从 6 改成 28。
② 留言带附件
服务端网页端早就有(tasks/comment_add 里的 attach_save_files),
但 **API 的 comments 端点只收 content**,且空文字直接 422
⇒ 端上根本没有传附件的入口。三处一起改:
· app/ApiHandler.php:comments POST 支持 multipart
- 有 file 就走附件流程:先 addComment 拿 cid,再 Attachment::save
(顺序不能反:save 要 owner_id,cid 是插入后才有的)
- kind 用 task_comment,与网页端 attach_save_files 同一归属类型
(不同的话同一条留言的附件在两端互相看不见)
- **允许只发附件不写字**:自动补占位文案「(附件)」,与网页端同口径
· FbTaskRepo.addTaskCommentWithFile():手拼 multipart
- 不能复用 fbUploadOne(那个是"一个文件 + 一个字段",
评论要同时传 content + file)
- 文件段必须带 Content-Type:服务端按它判"是不是图片"
- boundary 之间用 \r\n(用 \n 有些解析器会把整段当一个字段,
表现为"文字上去了、附件不见了")
· FbTaskPanel:
- 「+ 添加附件」按钮(DocumentViewPicker,任意文件)
- 选中的文件名显示成 chip,可点 ✕ 去掉
- 附件**不立刻上传**,随留言一起发(与「任务附件」那块语义不同:
那个传完就生效)
- 发成功后清空待发附件,否则下一条会重复带上
实测(模拟上传链路):
只有文字 => cid=18 ✅
文字 + 附件 => cid=19,附件数 1 ✅(key=files/6b4e...txt)
只发附件(占位) => cid=20 ✅
测试数据已清理。
|
2026-11-05 19:25:32 +08:00 |
|
laoyang
|
ad25792732
|
选人改「只留搜索框」+ 附件用系统打开 + 留言头像与主题色 (#77)
用户 2026-11-05 五项。
① 选人慢 → 未输入关键词时不渲染任何人
用户:「实在不行,我们就不把人列出来了,只留搜索框…输入拼音首字母就可以找到这个人」
慢的根因(不只是节点多):
· 名册 **800 人**(不是 8 个);
· 每人一个 FbAvatar,而它内部调 fbImageSrc(avatar, token) ——
那是**同步**查本地缓存 + 拼 URL,800 次全在打开弹窗那一帧跑完;
· 再加 800 个 Column/Text 节点参与布局。
⇒ 未输入时一个候选都不渲染,只显示"输入名字或拼音首字母找人"。
只有真打了字才过滤、才渲染(结果通常 1~20 个,瞬间出来)。
⚠️ 没改成"只显示前 N 个":800 人里翻前 20 个没用,用户还是得搜。
⚠️ 暂存区(已选那几个)不受影响:数量极少且必须一直可见
(否则用户忘记选了谁)。多选 + 多次搜索的流程因此保持完整:
搜一个 → 点选进暂存 → 改关键词再搜 → 再点选 → 确定。
② 附件:Word 等非图片文件交给系统应用打开
用户:「应该打开本机上默认能够打开这个文件的应用,让它把文件打开才对。」
项目里**已经有** common/FbAttachOpen.ets(下载明文到 cacheDir +
隐式 Want + action.viewData 交给系统「打开方式」),只是任务面板没调用。
⇒ 附件缩略图挂上 onClick:
· 图片 → 就地全屏预览(新加浮层,ImageFit.Contain 看全图不裁边)
· 其它 → fbAttachOpen(),系统自己找能打开 .docx 的应用
⚠️ 不能用「显式 Want + 包名」:不知道用户装的是 WPS 还是 Office。
⚠️ 预览用 Contain 而非 Cover —— 缩略图用 Cover 对,预览裁边就错了。
③ 留言人头像
服务端 Task::comments() 的 SQL 里**一直有** `p.avatar`,
端上 parseTaskComment 只取了 name/color ⇒ 头像从没显示过。
补 RawTaskComment.avatar + FbTaskComment.avatar + 解析,
UI 换成公共 FbAvatar(有图用图、没图退回首字色块)。
④ 留言标题/发言人用模块主题色加粗
原来发言人是 muted 灰、常规字重 —— 漏了全站「以模块色标识」的规矩
(观影紫 / 旅程绿 / 任务赭红)。改成 this.accent + FontWeight.Medium。
同时把「附件」「留言」「子任务」三个小节标题也从 ink 改成主题色。
⚠️ 正文**没有**跟着改主题色:整段上色会掉可读性(色彩需要留白)。
如果用户要求正文也变色,改一行即可(已在代码注释里标了位置)。
⑤ 编译通过,已装真机。
|
2026-11-05 19:19:57 +08:00 |
|
laoyang
|
4e2fbb04b7
|
鸿蒙任务面板:看板平铺、截止负责人备注可改、文案与图标 (#77)
用户 2026-11-05 对鸿蒙端首页任务弹窗提了 12 项,本批完成 9 项
(附件预览/下载单独做)。
① 看板平铺,不再左右滑
参考 FbTripNodeSheet 的做法:≤6 项 Flex(wrap) 换行平铺,>6 项才横滑。
当前家庭 4 个看板(默认 + 正在进列/等待反馈/等待结果)⇒ 走平铺。
⚠️ 平铺必须用 Flex(wrap),ArkUI 的 Row 不换行,塞不下会横向溢出
父容器(v76 真机踩过:最后一个 chip 被裁掉且点不到)。
⚠️ Row 的 space 在 Flex 里不生效 ⇒ chip 补了 margin 做间距。
② 删掉截止下面重复的「看板」行
上面 boardChips() 已经把当前看板高亮出来了,再列一行纯属重复,
而且两处都要同步才不出错。
③ 截止日期可改
点整行 → FbDateTimePicker(与编辑表单同一个组件,交互一致)。
改完立刻保存,不设保存按钮 —— 与状态/看板/子任务同一套交互。
④ 负责人可改 + 头像
点头像/名字 → FbPersonPickSheet 单选。
头像来源:服务端 Task::find 的 SQL 里**一直有** a.avatar AS assignee_avatar
(实测 #49 返回 avatars/2026xxx.jpg),只是端上 parseTaskItem 从没解析
⇒ 补 RawTask 字段 + FbTaskItem.assigneeAvatar/assigneeColor + 解析。
没头像时用首字色块兜底(与留言区同一手法)。
⑤ 备注可改
点一下变输入框,回车或失焦自动保存(等用户去找"保存"按钮
就会以为没存上)。
⑥ 「共享给谁」→「协作人」(标题与弹层标题都改)
⑦ 「+ 加图片」→「+ 添加附件」,**行为也跟着改**:
原来 pickAttach 用 PhotoViewPicker + IMAGE_TYPE 只能选相册图片,
改叫"添加附件"却选不了 PDF/Word,用户会以为功能坏了
⇒ 换成 DocumentViewPicker(系统文件选择器),能选任意文件。
⑧ 留言区标题加 💬 图标
⑨ 「回车发送」从 placeholder 挪到下方独立小字
(塞 placeholder 里一打字就看不见,而且 placeholder 该放"填什么"不是"怎么操作")
⑩ 发送按钮显示不全
根因:写死 width(56) + textAlign(Center),Text 默认内边距吃掉两侧空间,
56 减去内边距 + 两个汉字正好压线,字体缩放或不同机型就截掉半个字。
⇒ 改 constraintSize({minWidth:60}) + 水平 padding,按文字实际宽度自适应。
⚠️ 另一个必须同步的地方:FbTasksPage 里改状态时会**整条替换** FbTaskItem,
新加的两个字段漏了就会被抹掉(改一次状态头像就没了)——已补。
编译通过。
|
2026-11-05 19:12:24 +08:00 |
|
laoyang
|
5fba8c4eda
|
修导航目的地前面多出问号 (#69)
用户 2026-11-05:「导航的,点进去导航之后,为什么目的地地址前面有个问号」
根因:我上一轮拼的是 `lat,lng?地名` —— 那个 `?` 是照抄 `geo:` URI 的写法,
但这个字符串**不是 URI**,是交给 fbOpenNavigation() 的普通文本。
FbNav.parseGeoOf() 的动作:
① 正则抓 `数字,数字` 当坐标
② clean = 把坐标从原文剪掉后剩下的文字,用作地名
传 `36.168977,120.375693?青岛北站` 剪掉坐标后剩 `?青岛北站`
⇒ 问号跟着进了高德 dname,目的地标题显示成「?青岛北站」。
改成「地名 + 空格 + lat,lng」:
`青岛北站 36.168977,120.375693`
剪掉坐标后剩「青岛北站」,正是要显示的名字。
实测(复刻 parseGeoOf 跑):
旧 lat,lng?地名 → clean=[?青岛北站] ❌
新 地名 lat,lng → clean=[青岛北站] ✅
只有地名 → ok=false 走地理编码 ✅
两处都改了:
- FbEventSheet.navTargetOf()(#69 新增的)
- FbDayEventPanel 行程卡片的 onClick(#67 里同一个错误)
|
2026-11-05 17:22:36 +08:00 |
|
laoyang
|
ad9b934760
|
日程详情卡:补齐行程的出发地/目的地 (#69)
用户 2026-11-05:「点击行程里旅行日历本里的信息时,弹出来的那个框是在首页上
弹出的,还是没有出发地和目的地。」
根因链(三段,缺一段都不显示):
1. FbCalEventFull 没有那四个字段
#67 只给了 FbCalEvent(列表用),详情卡消费的是 FbCalEventFull。
2. shellOf() 只塞了 location: b.place
行程节点的 place 是空串 —— 它的位置概念是"从哪到哪",没有单一地点。
3. 最关键:行程条目**永远走 shell,压根不请求详情接口**
openEvent() 里 `if (!isEditableEvent(e)) { shellOf(e); return; }`,
行程的 kind 是 trip 不是 custom ⇒ 直接短路。
而且即使去请求也没意义:Calendar::event($id) 查的是 calendar_events,
行程节点在 trip_nodes 表,那个 id 查不到东西。
⇒ 位置信息**只能**靠 shell 带过去。
改动:
- FbCalEventFull 加 fromPlace/toPlace/fromLng/fromLat/toLng/toLat
- FbCalRepo.toFull() 从接口读(接口给就填,普通日程取到空串)
- 首页 FbHomePage.shellOf()、日程页 FbCalendarPage.shellOf() 两处传递
(两处是独立实现,只改一处会漏)
- FbEventSheet:
· navRow 改 navRowRaw(label, place, navTarget) —— 标签不再硬编码「地点」
· onNavigate 从无参改成 onNavigate(navTarget)
原来宿主只能读 event.location 猜导航目标,无法区分出发地/目的地
· 渲染三分支:有 fromPlace 画「出发地」、有 toPlace 画「目的地」、
有 location 画「地点」(三者互斥:行程条目只有前两个,普通日程只有第三个)
· 新增 navTargetOf(place, lng, lat):有坐标返回 `lat,lng?地名`
(同名地点不会搜错城市),没有就只给地名让 FbNav 地理编码
- 首页 navigateEvent / 日程页 navigateDetail 改成接受 navTarget,
非空直接用,为空退回原来那套推导(底部那颗「导航」按钮传空串)
|
2026-11-05 16:21:16 +08:00 |
|
laoyang
|
f30ea5b1e4
|
日程行程条目端上同步:字段、列表卡「出发地→目的地」可导航 (#67)
同步服务端 #67 的新字段与三处改动:
数据层
- RawCalEvent / FbCalEvent 加 fromPlace/toPlace/fromLng/fromLat/toLng/toLat
- FbParse 解析赋值;新增 numStr() 把 DECIMAL 可能给的 number 转成字符串
(`s()` 签名只收 string|null|undefined,坐标字段会报类型错)
- FbCalStore.toModels、DemoData 的 8 条演示数据同步补字段
渲染
- FbDayEventPanel 列表卡片:在时间行**上面**加一行「出发地 → 目的地」,
只对两端都有值的条目画(普通日程 fromPlace/toPlace 都是空串,不画)。
目的地用日历本色 + 下划线,点击 fbOpenNavigation:
有坐标走 `lat,lng?地名`(同名地点不会搜错城市),没有就让 FbNav 地理编码。
不塞进时间行是因为那里已有「时刻 + 重复徽标 + 地点」,窄屏会挤爆。
- FbDayTimeline 全天块:一行文字「出发地 → 目的地」,替代原来的 place。
不给点击 —— 26vp 高的块切两个热区点不准,导航入口在详情卡。
图标与 ✓ 的改动在服务端(Trip::leadIcon / 删 ✓ ),端上只消费 title,
所以不需要额外处理。
|
2026-11-05 16:12:14 +08:00 |
|
laoyang
|
c498037ec8
|
首页标题主题色补完 + 新建去加号 + 全天块上间距 (#60 #61)
用户二次反馈四项,全部是上一轮没做到位的:
1. 标题颜色没变
上一轮给 FbSectionTitle 加了 @Param accent(默认空串),
但首页 6 处调用点**一处都没传**。grep 到的 7 个 accent:
全在 FbEventSheet/FbTaskPanel/FbDayEditSheet/FbMovieDetailSheet/
FbPersonSheet 上 —— 那些组件本来就自带这个参数,与标题无关。
空串 ⇒ titleColor() 退回 #8A7A6D 灰 ⇒ 组件改得再对也不变。
补 5 处:日程/任务/纪念日/观影/联系人各自 fbModuleOf().brand。
往年的今天不传(混合来源,没有单一模块色)。
2. 条数与"新建"贴死
extra 后面直接接胶囊,Row 无 space、胶囊无 margin。
加 margin({left:12})。不用 8:胶囊有实体底色,视觉比纯文字更紧。
3. 去掉「+」前缀
5 处 addText 去加号;连带 3 处空态提示的「点右上角「+ 新建」」
同步改掉,否则提示里带着按钮上已不存在的符号。
4. 全天日程问题在上面不在下面
上一轮改 bottom 6->12,方向反了。
FbDayEventPanel 给 FbDayTimeline 的 padding 没有 top
⇒ 第一个全天色块上边直接压在白卡片顶边上。
改成 {top:10, bottom:12}。只加在有全天的分支里。
|
2026-11-05 15:52:40 +08:00 |
|
laoyang
|
c11548cb51
|
首页改造:概述跳锚点、主题色、详情卡、点击响应 (#59 #60 #61 #62 #63 #64 #65)
#59 概述四格数字可点击跳锚点
- 加 Scroller + yEvents/yTasks 坐标字段
- 两个区块挂 onAreaChange 记录 y 偏移
- FbStatCell 加 onTap(整格热区,不是只有数字两个小字)
- 今天日程 -> 今天日程区块;在做/任务/逾期 -> 近期任务区块
(三者都是任务状态计数,点哪个都是想去看任务,让"逾期"单独跳
一个不存在的"逾期列表"是骗人的)
- 概述数字本来就来自 d.stat.*(接口真实数据),无需改
#60 各区块标题用模块主题色 + 新建改胶囊按钮
- FbSectionTitle 加 accent 参数(默认空 = 保持原灰,老调用点不动)
- 标题有主题色时加粗一档(光换色辨识度不够)
- 新建从文字链接改成胶囊按钮:底色主题色、白字、全圆角
- 6 处调用点分别传 calAccent/taskAccent/daysAccent/moviesAccent/peopleAccent
#61 全天日程与下方白块无间距
- FbDayTimeline 全天块 bottom padding 6 -> 12
- 6vp 在全天色块实心背景下几乎看不出缝;12 才有明显呼吸
- 与 Column(space:4) 的行间距形成「行内紧、块间松」层次
#62/#63/#64/#65 四个区块加"先看后改"详情卡
根因:5 个 @Builder 全都没有 onClick(memoryRow/movieRow/recentDayRow/
personTile 各自 0 个),所以用户"点了没反应"。
- 新增状态:mvDetail/mvDetailLoading/mvDetailError/mvSeed/pfDetail/
dayDetail/memDetail,全部计入 sheetCount(否则按返回键直接退出页面)
- openMovieDetail 照 FbMoviesPage.openDetail 的成熟写法用
FbMovieRepo.inst().detail(),不自己拼接口(parseMovieDetail 不导出)
- 复用三张现成卡:FbMovieDetailSheet / FbPersonSheet / FbDayEditSheet
- 往年今天按 kind 分发:movie 走电影卡;trip_node 拿不到 tripId
⇒ 退回通用摘要卡(弹错类型的卡比不弹更糟)
- memorySheet 用 FbSheetRing 就地画,内容可滚动(用户"滑下去看信息")
联系人头像本来就有(personTile 里 if (p.avatar !== "") 分支),无需改。
|
2026-11-05 13:43:03 +08:00 |
|
laoyang
|
aafd83b130
|
悬浮栏选中底圆角:999 -> 12,不再那么圆鼓 (#58)
用户 2026-11-05:「颜色我感觉透明度什么的还是可以的,但是这个圆有点太圆了,
应该弄成更椭圆一点,效果可能是不是更好啊?」
borderRadius(999) 会把 40 高的块钳成两端完整半圆,看着很圆鼓。
改成 12:圆角半径只占高度的 30%,是"柔和的圆角矩形",
视觉更平更舒展,也与 dock 外框的大圆角(28)形成内外层次。
ArkUI 的 borderRadius 是等比圆角(四角半径相同、正圆圆弧),
没有"椭圆角"(x/y 半径不同)的写法 —— 所以"更椭圆"只能靠减小半径近似,
半径越小圆弧越平。
实机偏圆就降到 10,偏方就加到 14,改动只有这一个数字。
|
2026-11-05 13:25:06 +08:00 |
|
laoyang
|
5d9902bb82
|
底部悬浮栏:选中项加灰色椭圆底 (#58)
用户 2026-11-05:「当选中其中一个按钮之后,它除了图标和文字颜色发生变化,
还给它加了一个那种椭圆的那种框那种背景,灰色的。这样更能清晰表示
现在正在被选中是哪一个功能。」
原来选中态只有变色 + 加粗,没有背景,观感偏弱。
结构上关键:不能直接给 Column 加 backgroundColor + borderRadius ——
那个 Column 是 layoutWeight(1),椭圆会撑满整格,看起来像"选中了一整块"。
套两层:外层等分格(透明,负责布局与热区)+ 内层椭圆(自适应内容宽高)。
底色用半透明黑 #14000000 而不是实色灰:
dock 底色是 EBFFFFFF(92% 白叠模糊层),实色灰在深色系统主题下会突兀;
半透明黑与背景混合,浅色下浅灰、深色下深灰,两种主题都协调。
borderRadius 用 999 而非固定值:固定值在标签变长时圆角比例会失调。
|
2026-11-05 13:21:34 +08:00 |
|
laoyang
|
0c2cf9b5e7
|
表单规范全量铺开 + 编辑时视频附件不显示 (#56)
① 编辑行程的视频附件不显示
根因:照片墙对所有附件都用 Image 渲染,视频是 .mp4,
Image 加载不出来 ⇒ 空白块 ⇒ 用户以为"视频没带进来"。
按 mime 分流:video/ → 深色底 + ▶(不取首帧,端上解视频首帧很卡)。
② 新增共享组件 FbFormField.ets(GLOBAL-FORM-SPEC)
5 个表单原来各写一份 field()/labelRow(),改一处要改五处,
而且外层结构不一致(有的 Row 有的 Column),批量脚本改坏过两次。
收敛到一个组件,表单只调用不自己实现。
③ 全量铺开
- FbPersonEditSheet 姓名标必填(红色无星号)
- FbEventEditSheet 日程 field 两排
- FbMovieNewSheet 片名/影院/同看的人/海报/观影照片/简介/备注 全部两排
- FbDateTimeField FbDateTimeRow 改两排(电影+日程同时生效)
规范:标签主题色 + Bold + fontSmall,必填红 #C0533F 无星号,
标签独占第一排、控件第二排整宽。
|
2026-11-05 12:52:51 +08:00 |
|
laoyang
|
ffd6ac5289
|
编辑行程加附件:用序号强制重建浮层,修回填不全 (#55)
根因:@Param editing 在 V2 里是单向同步 —— 父组件改 editNode 后子组件
的 editing 会更新,但 aboutToAppear 只在首次创建时跑一次。
⇒ 换一条行程再点编辑时,editing 变了但回填不重跑 ⇒ photos 保留上次的值。
修法:加 nodeSheetSeq,点编辑时 +1,组件用 .id(nodeSheet_N) 绑定序号。
序号变化时 ArkUI 销毁旧组件、创建新组件 ⇒ aboutToAppear 必然重跑。
顺带:返回键关闭时也清 editNode,避免下次走到复用路径拿到上一条的 editing。
|
2026-11-05 12:45:43 +08:00 |
|
laoyang
|
86de59616c
|
端上:#47~#55 打包 —— 行程双轨、动态类型、表单全局规范 (#53)
#48 计划/实际双轨:nodeAction(start/done/cancel),完成即转实际行程,
实际行程不再显示状态;新增 actualStartedAt/actualEndedAt
#49 类型/出行方式/车辆 chip 改内部读状态(@Builder 值传参不刷新)
#50 类型点击立即变色、出行方式平铺、参与人显示已选头像
#51 节点类型动态加载(原硬编码5种)、地址 onPick 拿详细地址与坐标
#52 标签改主题色 Bold、必填红无星号、编辑行程标题同步
#53 表单两排版式(标签独占一行解决左右对不齐);电影弹层 layoutWeight 修滚动
#54 地址联想框加时间窗抑制(600ms 内不搜)、候选限高 180
含附件回填诊断日志(DIAG-ATTACH/FILL),待真机确认后移除
|
2026-11-05 12:41:04 +08:00 |
|
laoyang
|
88fa38ecf9
|
导航修复:加系统地理编码拿坐标,改用 openLink 拉起高德
## 用户反馈
「智橙的导航那个地方功能不好用,跳转到高德,说是高德提示说什么不支持。
我记得同步日历它那个是支持的,所以你看一下同步日历是怎么实现的。」
## 对照 SyncCalendar(实测能用)找出两个致命差异
| 环节 | SyncCalendar | 我们原来的实现 |
|---|---|---|
| 拿坐标 | `geoLocationManager.getAddressesFromLocationName()` | 无(只能从地点文字正则抠) |
| 坐标系 | WGS84 → GCJ02 本地转换 | 无 |
| 高德 URI | `amapuri://route/plan/`(**带尾斜杠**) | `amapuri://route/plan`(无斜杠) |
| 无坐标时 | 仍给 dname 尽力而为 | 退到 `amapuri://poi?keyword=` |
| 拉起方式 | `ctx.openLink()` | `startAbility({uri})` 隐式 Want |
**为什么高德报「不支持」:**
1. **没有坐标**。`route/plan` 的 `dlat/dlon` 是**必填**,
我们只在"用户恰好把坐标粘进地点文字"时才给 ⇒ 绝大多数情况给不出。
2. **退路 `amapuri://poi?keyword=` 在新版高德已废弃** ——
那正是「不支持」的直接来源。
3. URI 少了尾斜杠(`route/plan` vs `route/plan/`)。
## 另外:原文件有一条未经验证的错误结论
原文写着:
「拉起方式用 startAbility 的隐式 Want,而不是 openLink:
openLink 走的是 App Linking,会报 16000019 No matching ability is found」
但 SyncCalendar **就是用 openLink 拉起高德的**,而且它的 `module.json5`
**根本没声明 `querySchemes`** —— 说明 openLink 不需要那套声明。
原结论是推断、未经实测,本次改用 openLink。
## 改动
### FbNav.ets
- 新增 `geocodeBySystem()` —— 系统正向地理编码拿 WGS84 坐标
- 新增 `wgs84ToGcj02()` —— 火星坐标转换(**必须转**,否则偏 100~700 米,
而且不报错,只是位置不对,比"打不开"更难排查)
- 新增 `openLinkWithFallback()` —— openLink 优先,失败退 startAbility
- `fbOpenNavigation()` 改为 async:先求坐标,再依次试各家地图
- 高德 URI 修正:`amapuri://route/plan/?sourceApplication=FamilyBook&dlat=..&dlon=..&dname=..`
- 关键字退路改成 `route/plan/?dname=` 形式(旧 `poi?keyword=` 已废弃)
### 两个调用方补 catch
`fbOpenNavigation` 变 async 了,不接住 rejection 会报未处理异常。
## 不需要新权限
`getAddressesFromLocationName()` 是「地址→坐标」换算,不是读设备位置,
不需要 `ohos.permission.LOCATION`。SyncCalendar 也没声明,实测可用。
## 验证
- 编译:`build_hap debug` → BUILD SUCCESSFUL
- **真机验证未做**(设备未连接)。需要确认:
· 点地点能否直接进高德**路线规划页**(不是搜索页)
· 目的地是否**位置准确**(验证 GCJ02 转换生效)
· 没装高德时是否正常降级到下一家 / 复制地点
## 保留
多地图探测(高德/百度/花瓣/腾讯)、复制地点兜底、
「坐标失败 → 关键字重试」的链都保留。
|
2026-11-03 10:28:28 +08:00 |
|
laoyang
|
0397270e8b
|
日程改为本地库按需查询,修掉每次进页面解析 9.3MB 的架构错误
## 用户指出的问题
「这个数据量很大,不是应该每次同步完了之后解包放在一个数据库里吗?
然后点开日程,不就是把今天的日程从数据库里查出来吗?
这个应该速度非常快才对呀!怎么能每次打开都要去读所有的数据呢?」
用户说得完全对。这是**架构错误**,上一轮我只做了算法层的局部优化
(把 O(n²) 改成 O(n)),没动"数据怎么存、怎么取"这个根本问题。
## 改前 vs 改后
改前:
load() → module_cache 表读 **9.3MB 字符串**
→ JSON.parse **28628 个对象**
→ 全塞进 this.allEvents 内存数组
渲染 → 在这 28628 条上遍历
`module_cache` 的设计注释本来写着「解析一遍毫秒级,可接受」——
那是按小数据量假设写的,日程涨到 2.8 万条后这个假设就破了。
改后:
load() → ① `WHERE date BETWEEN ? AND ?`(走 idx_cal_date)→ 立刻渲染
② 后台拉全量 → 批量入库 → 重查当前范围
渲染 → 只在**当前范围**那几十到几百条上遍历
## 改动
### FbSchema / FbDb:新增 cal_events 实体表
字段对齐 FbCalEvent(12 个)+ uid + fetched_at,三个索引:
- `idx_cal_date` —— 按日期查(最高频)
- `idx_cal_date_kind` —— 农历/节日按 kind 过滤
- `idx_cal_uid` —— 同步去重
⚠️ 主键不用服务端 `ref_id`:衍生条目(农历/节气/纪念日)的 ref_id **恒为 0**,
几万条全撞主键。用自增 rowid + (cal_id, uid) 唯一索引。
uid 由 kind|calId|evId|date|time|title哈希 组成。
⚠️ **建表语句与 migrate 两处都改了**,列名逐一比对一致
(这个坑本项目已踩 5 次,加了静态守卫钉住)。
### FbCalStore(新建):本地库读写
- `replaceAll(rows, stamp)` —— 事务内先 DELETE 再分批写(20 行/批,照
FbMovieRepo 的实测结论,200 行会卡死)。整批替换而非逐条 upsert,
否则服务端已删的条目会永远留在本地。
- `between(from,to)` / `ofDay(iso)` —— 页面真正用的查询
- `lunarBetween` —— 月视图 42 个格子只要农历文字,单独查比取全量再过滤快
- `count()` / `fetchedAt()` —— 判空与"数据新不新"
⚠️ 命名用 `FbCalStore` 而不是 `FbCalRepo`:后者已存在,是**接口读写**层
(create/update/remove/fetchBooks)。我第一版误覆盖了它,编译报 8 处错误后
从 git 恢复,改名为 Store 区分「本地库」和「接口」。
### FbCalendarPage
- `allEvents` → **`rangeEvents`**(改名强制提醒:这里不再是全量)
- `rangeOfView()` —— 算当前视图需要的日期区间
- `loadFromLocal()` —— 先查本地,有数据立刻渲染
- `persistAndRefresh()` —— 入库完成后重查,界面跟上最新数据
- `reloadRange()` + `@Monitor('showYear','showMonth','mode')` —— 翻月/切视图按需重查
- `rangeSeq` —— 防抖,避免旧结果覆盖新结果
## 验证
### 编译
`build_hap debug` → BUILD SUCCESSFUL
### 静态校验(34 项全过)
`_tooling/check_cal_db.js`,含最关键的两项:
- 建表与迁移**列名逐一相同**(15 列全对上)
- `load()` 里本地查询确实在远程同步**之前**
### 未验证
**真机实测未做**(设备未连接)。需要确认:
- 老库升级(v7→v8)是否顺利建表
- 首次进入(库空)要等全量拉取,之后进入应该秒开
- 翻月是否立刻有数据
## 过程中的一个失误
第一版把新仓储命名为 `FbCalRepo`,**覆盖了已存在的同名文件**。
编译立刻报 8 处 "has no exported member / does not exist on type"。
从 git 恢复原文件后改名 `FbCalStore`,避免了两层职责混在一个类里。
教训:新建文件前先查同名是否存在(`git status` 没报是因为该文件本就在版控里,
我 `cp` 上去把它改成"已修改"状态,看着像正常改动)。
|
2026-11-03 09:59:40 +08:00 |
|
laoyang
|
57a0ddd5ad
|
日程页加载慢:O(n²) 改 O(n) + 全量范围收窄(A+B)
## 问题
用户:「首页点悬浮按钮进日程,加载得好几秒」。
实测数据:`calendar_events` 2026 条 + 328 条 rrule,按范围展开后 **28628 条**。
## 根因:渲染路径上有 O(n²)
| 函数 | 每次成本 | 调用次数 |
|---|---|---|
| `lunarOf(iso)` | 遍历 28628 条 **×2 遍**(先找节日、再找农历) | 月视图每格 + 周视图每天 + 列表每天 |
| `visibleEvents()` | **新建数组** + 遍历 28628 条 | `eventsOf()` 每次调用,列表按天调 |
月视图 42 格 × 2 遍 × 28628 ≈ **240 万次比较**,全部同步跑在 build 路径上。
## A:索引化(客户端)
- 新增 `ensureIndex()`:**一次遍历**同时建 `lunarIdx`(iso→农历文字)
和 `dayIdx`(iso→当天条目,已排序)
- `lunarOf()` 改为 O(1) 查 Map
- `eventsOf()` 改为 O(1) 查桶,返回副本(防止调用方 sort/push 改到索引)
- 缓存失效:`evtVer` 计数,`allEvents` / `books` 变化时 `++`;
`idxVer` 记录索引对应版本,不一致才重建
⚠️ **语义必须一致**:旧 `lunarOf` 是「先找 traditional/solarterm/holiday,
找不到才用 kind=lunar」。建索引时用两个 Map 分级收集、最后按"节日优先"合并,
否则会出现「初一」盖掉「中秋」。
保留了这些特例:
- `calId === 0` 的衍生条目不受"本可见性"控制
- `books` 为空时全部通过
- 桶内按 `byTime` 排序
## B:范围收窄 + 服务端上限
### 客户端
`CAL_FULL_FROM/TO` 写死的 `1970-01-01 ~ 2100-12-31`(47846 天)
改为 `calFullRange()` 动态算「今年 ±5 年」(`CAL_SPAN_YEARS = 5`)。
理由:
- 服务端要为**每一天**生成农历条目 ⇒ 130 年范围要传上万条(约 6MB)
- 实际使用只看眼前几年
- 动态计算而非写死年份,避免随时间推移失效
- 缓存键仍带 from/to ⇒ 跨年后范围平移、旧缓存自然失效
### 服务端 `app/Holiday.php`(三处都修)
| 函数 | 原问题 | 修法 |
|---|---|---|
| `lunarRange()` | `$i < 400` ⇒ 只算前 400 天,**今天的农历空** | 去上限,改年数上限 30 年 + 夹 Lunar 支持范围 |
| `traditionalRange()` | 同样 400 天 ⇒ **今年春节/中秋全丢** | 同上 |
| `solarTermRange()` | 只取首末两年 ⇒ 130 年仅 2 年节气 | 改逐年遍历 + 年数上限 |
`MAX_LUNAR_YEARS = 30` 是防御性的:客户端只要 ±5 年,
留余量足够;同时防止有人手拼 1900~2100 把服务器拖死(算 7 万天农历需数十秒)。
## 验证
### 服务端实测(部署后)
```
今天 2026-11-02 → 「廿四」 ← 修复前为空
本月农历覆盖 30/30 天
今年 11 个传统节日、24 个节气 ← 齐全
2021~2031 农历 4017 条(应 4017) ← 修复前只有 400 条
2021~2031 节气 264 条(应 264) ← 修复前只有 48 条
1970~2100 农历 10958 条,0.47 秒(按 30 年上限截断,符合设计)
```
> ⚠️ 中途发现改动**没上传到服务器**就跑了验证,一度得出"已修好"的错误结论。
> 比对 md5 才定位到。现在 `_tooling/check_cal_perf.js` 里有这项检查(7.1)。
### 静态校验
`_tooling/check_cal_perf.js`:**36 项全过**,含
- 渲染路径无全表遍历
- 索引语义与原实现一致(8 项)
- 缓存失效正确
- 范围收窄且动态
- 服务端三处上限已修
- 服务器文件 md5 与本地一致
### 编译
`build_hap debug` → `BUILD SUCCESSFUL`
### 未验证
**真机加载耗时未测**(设备当时未连接)。理论提速约 84 倍
(240 万次比较 → 28628 次遍历 + 42 次查表),但实际感知需真机确认。
## 顺带记录
服务端 `Holiday.php` 需要**单独上传**(它不在客户端仓库里)。
以后改服务端文件,验证前必须先比对服务器 md5,否则会拿旧代码验出假结果。
|
2026-11-02 08:51:28 +08:00 |
|
laoyang
|
274b363852
|
备案备注:按官方要求填写「实际开办内容」,含三档字数版本与避坑词表
|
2026-11-01 23:11:48 +08:00 |
|
laoyang
|
a808ae5f42
|
备案材料:更新为真实 Release 证书数据,修正取错根 CA 的 MD5
## 修正的错误(要紧)
旧材料里的签名 MD5 是 `7d0c4b46b7c1c0d23c6e4316e5c996a3`,
那是**华为根 CA 的指纹**,不是开发者证书的。
原因:`.cer` 是三层证书链(根 CA → 中间 CA → 你的证书),
`openssl x509 -in xxx.cer` 只读第一张,取到的就是根 CA。
这个值若填进备案系统,签名校验必然失败。
正确做法是先拆链再取最后一张:
先按 BEGIN/END CERTIFICATE 切分成 m0.pem m1.pem m2.pem,
再 `openssl x509 -in m2.pem -noout -fingerprint -MD5`。
## 本次实测值
| 项 | 值 |
|---|---|
| 包名 | familybook.yangyq.net |
| 证书主体 | CN=杨永全(1845816276425949633), Release |
| 有效期 | 2026-11-01 → 2029-11-01 |
| 签名 MD5 | 95d2b3d31de21fad563ee8f71df16c65(已去冒号、小写) |
| 公钥 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEU+Of... |
## 顺带
- APP 名称统一为「岁月家」
- 删掉旧文件 APP备案材料.md(含错误 MD5,留着易误用)
- 补充三项提交前必查前提:服务器付费方式、子域名备案、正式证书
- 服务内容说明按工具类口径写好,避开前置审批
|
2026-11-01 23:08:34 +08:00 |
|
laoyang
|
b32501535e
|
日程改为全量同步 + 修复大响应超时(laoyang/FamilyBook#31)
## 背景
用户 2026-10-30:「全部拉取,全量同步」。
起因:切到下个月再点某一天,端上没数据(即使服务端有)。
根因是请求范围写死成「今天-7 ~ +60」且切月不重拉 ⇒ 窗口外的日子永远空。
## 改动
### FbModuleLoad.loadModule
- `name` 参数从 `FbModuleName` 放宽到 `string`
日程改成全量后,缓存键要带请求窗口(`calendar:1970-01-01:2100-12-31`),
否则"旧窗口的数据"会被当成新窗口的数据显示。
- 新增可选 `timeoutMs`
全量约 9.3MB / 28628 条,默认 15 秒在移动网络下会超时;
超时后 `FbModuleRepo` 退回旧缓存,表现成"翻月没数据"且极难查。
### FbModuleRepo.load
- 透传 `timeoutMs` 给 `FbApi`。
### FbCalendarPage
- 请求改为全量范围(`CAL_FULL_FROM` ~ `CAL_FULL_TO`),超时放宽到 60 秒。
- 缓存键带窗口(`calendar:<from>:<to>`):某次请求失败后 `FbModuleRepo`
会把**旧范围**的缓存 sink 回来,端上就把"只含 10~12 月的数据"当全量显示
—— 这是该 bug 的帮凶,加窗口标识后从根上消失。
- `agendaDays()` 的 `-7 ~ +60` 现在**只决定列表显示多少天**,
不再兼作请求范围(原来两个数必须同步的约束随全量同步作废)。
- 删除 `agendaFrom()` / `agendaTo()`(不再需要按当前日期算窗口)。
### 恢复 canEdit 的写权限判断
本轮改动曾把
`isEventWritable(this.detailBase, this.books)`
回退成
`isEditableEvent(this.detailBase)`
这是**工单 #31 已修过的 bug 回退**:只判 kind/id/calId 不看本的可写性,
导致只读共享日历里的日程也显示「编辑」按钮,用户改完点保存才报
「这本日历你没有写权限」。
已恢复,并与首页 `FbHomePage.ets:1517` 保持同一口径
(否则同一事件"从首页进能编辑、从日历页进不能")。
## 验证
`build_hap debug` → `BUILD SUCCESSFUL`(33 tasks)。
⚠️ **未做真机验证**。全量同步的实际耗时与内存占用需在真机上确认
(28628 条约 9.3MB,解析 150~300ms,主要成本是流量)。
## 备注
这 3 个文件的改动来自 2026-10-30 那一轮,因故未提交;
本次一并整理提交,与品牌改名(家书→岁月家)无关。
|
2026-11-01 22:54:22 +08:00 |
|
laoyang
|
d2129c9aab
|
配置 AGC Client ID(Map Kit 地点联想与 Account Kit 登录都依赖它)
原值是可读的占位符 REPLACE_WITH_AGC_CLIENT_ID,占位期间:
- Map Kit 联想不返回任何候选(错误 1002600003 App authentication failed)
- 华为账号手机号登录也会失败
⚠️ Client Secret 不入库:它是服务端凭据,纯鸿蒙客户端不需要,
且一旦进 git 历史就无法真正清除。
|
2026-11-01 20:44:50 +08:00 |
|
laoyang
|
825008657b
|
品牌改名:家书 → 岁月家 (laoyang/FamilyBook#38)
## 起因
用户 2026-11-01:「家书已经有人做了,八月份刚上线的」。
核实:「家书 HomeBook」(广州家书科技有限责任公司,粤ICP备2026096420号,
2026-06 成立、08 上线)。功能**部分重合**(家庭记录 + 相册 + 家谱)。
华为审核指南 1.3「应用名称不得和其他应用名称相同,避免混淆」,
虽然个人与企业主体查重不互通、技术上能过,但被人工驳回概率高。
## 定名过程(用户逐个否掉)
| 候选 | 否决原因 |
|---|---|
| 寸草 | — |
| 家卷 | ❌ 与「家眷」同音,口头传播会打错字 |
| 家时光 | ❌ App Store 已有「家·时光」定位几乎一致;且「家+时光」是常见组合,辨识度低 |
| 家纪 | ❌ 已有重名 |
| **岁月家** | ✅ 搜不到同名应用;是口号的直接展开(岁月 + 家) |
## 最终定稿(用户明确指定)
```
应用名 岁月家
主标语 一家人用岁月写成的一本书 ← 不变
一句话简介 一家人用岁月写成的一本书 ← 改为与标语相同
官网标题 岁月家 · 一家人用岁月写成的一本书
英文名 FamilyBook ← 不变
包名 familybook.yangyq.net ← 不变
```
## 改动范围(17 + 18 处,12 个文件)
### 应用名(17 处)
`AppScope/resources/base/element/string.json` 的 app_name,
`entry/.../string.json` 的 16 条(应用名、卡片名、无障碍描述)。
### 用户可见文案(18 处)
`FbBackgroundSync` / `EntryAbility`「正在同步家书数据」、
`FbAttachOpen`「家书附件」相册名、`FbHttp` 地址校验失败提示、
`FbCardData` / `FbCardMediaData` 卡片空态「先在「家书」里填写服务器地址」、
`TopBar` / `Drawer` / `FbLoginPage` 的应用名文本、`FbSettingsPage` 的说明。
## 刻意不改
- **包名 / bundleName**(用户明确要求不变,且改了等于换一个应用)
- **英文名 FamilyBook**、目录名、仓库名(改了会让 git 历史、部署脚本、证书路径全跟改)
- **代码注释**里 200+ 处「家书 FamilyBook · 某模块」—— 不影响运行时,
改了只会制造噪音 diff,让将来 blame 变难
- **主标语**里一个字都没动
## 验证
### 编译
`BUILD SUCCESSFUL`,33 tasks。
### 产物实测
解包 `.hap` 的 `resources.index`:
- 「岁月家」出现 **17** 次
- 「家书」出现 **0** 次
### 全局一致性校验(31 项全过)
`_tooling/check_rename.js`:
- 客户端 app_name / EntryAbility_label / 产物内容
- .ets 用户可见文案无残留(排除注释)
- 服务端非注释代码无残留
- `Auth::homeTitle()` 用「某某某的家庭岁月」
- `DEFAULT_SLOGAN` 未被改动
- 官网线上 5 个页面无「家书」、标题含岁月家、渲染后 h1 是品牌名
- 材料 app_name / 一句话简介 / 包名 / versionCode / 域名
⚠️ 校验过程中我自己的判据出了 2 次假红:
① 注释识别不全(漏了 `/**` 单行块注释和 `<!--` HTML 注释)→ 补齐 isCommentLine
② 「为什么叫岁月家」整节重写后,旧断言还在查「书信」「家与书」
(那是**旧名字**的逻辑)→ 改成查「岁月」+「家」的拆解
## 一处语义决策(问过用户)
网页端 `Auth::homeTitle()` 生成「某某某的家书」—— 这是**产品核心语义**
(每个家有自己的那一本),直接换成品牌名会读成「某某某的岁月家」,不通。
用户定:改成 **「某某某的家庭岁月」**。
兜底(无姓名时)仍用品牌名「岁月家」。
## 连带影响(不阻塞)
- 上架材料 7 个文件已同步(含软著全称改为「岁月家家庭生活档案管理软件」)
- 官网已部署,线上标题已是新名字
- 软著申请尚未提交 —— 正好一次写对新名字,不需要变更
|
2026-11-01 19:55:36 +08:00 |
|
laoyang
|
c0d5fdff01
|
docs(上架): 应用商店上架 / 正式证书 / APP 备案 全套材料
## 背景
用户要同时办三件事:① 上架华为 AppGallery ② 申请正式发布证书 ③ 在阿里云做 APP 备案。
本目录是这三件事的全部材料,信息**全部取自真实代码与实测**,不是套模板。
## 材料清单
| 文件 | 用途 |
|---|---|
| `README总览.md` | 先看这个:材料索引 + 三件事的先后关系 + 你现在就能做的 |
| `商店上架文案.md` | AGC 各字段可直接复制的文案(一句话简介/应用介绍/更新说明/分级问卷/隐私标签) |
| `商店信息_结构化.json` | 同上,机器可读 |
| `隐私政策.md` | 完整隐私政策正文 |
| `用户服务协议.md` | 完整用户协议正文 |
| `正式证书与签名切换.md` | 发布证书申请 + release 打包全流程 |
| `APP备案材料.md` | 阿里云 APP 备案逐字段填写指引 |
| `图标/app_icon_216.png` | AppGallery 图标(216×216,9.7KB) |
| `图标/app_icon_1024.png` | 备案图标 + 1024 规格(112KB) |
| `verify_materials.py` | 校验材料与代码一致(42 项) |
## 实测核对过的事实(材料里的数字都从这里来)
- 包名 `familybook.yangyq.net`、版本 `0.8.0` / versionCode `800`(`AppScope/app.json5`)
- 应用名「家书」(`AppScope/resources/base/element/string.json`)
- 权限 **只有 3 个且全是普通权限**:INTERNET / GET_NETWORK_INFO / DETECT_GESTURE
(`entry/src/main/module.json5`)—— 定位、通讯录、相机、麦克风、设备标识符**一个都没申请**
- 第三方 SDK:**无广告、无统计、无推送**
- 后端域名 `family.yangyq.net`,服务器阿里云 `47.104.72.161`(中国大陆)
权限少是审核优势:「权限滥用」是驳回第一大类原因,家书直接绕开。
隐私政策第二节与第十节做了**权限逐条对照表**,可直接给审核看。
## 图标为什么要重做
包内 `fb_logo.png` 是 **192×192 且自带圆角**(四角透明,半径约 33px)。
AppGallery 两种规格都不达标(要 216 或 1024,且「square-shaped icon」)。
→ 取内部主色 #F2E9D8 铺满方形画布补掉缺角,图案原样叠加、不裁切不缩放,
生成 216 与 1024 两版。图案与包内完全一致(审核要求后台图标 = 包内图标同一图案)。
第一版直接用 LANCZOS 放大,**保留了圆角**,被 `verify_materials.py` 抓到并修正 ——
这就是写那个校验脚本的意义。
## 四个必须先解决的问题(已写进总览,不隐瞒)
1. **现在是调试证书** → 备案要正式证书的公钥/MD5,上架要正式签名(调试包报 991/1014)
2. **没有「家书」软著** → 备案不强制;华为要求但可用《免责函》过渡(6 项现有软著均无关)
3. **隐私政策页面还不存在** → 站点无 privacy 路由;建议用华为自带托管服务(SyncCalendar 用的那个,实测 200)
4. **阿里云服务器是否包年包月** → 备案硬性要求,需用户去控制台确认
## 顺序(三者有依赖,不能乱)
```
① 申请正式证书(10 分钟,AGC 即时出证)
↓ 拿到后才能
② APP 备案(1~20 工作日,最慢,先提交)
③ 打 release .app 包
↓ 拿到备案号才允许提审
④ 提交 AppGallery 审核(1~7 工作日)
↓
⑤ 公安联网备案(上架后 30 日内)
```
## 校验
`python3 verify_materials.py` → **42 项全过**:
包名/版本/名称与代码一致、权限集合完全一致、隐私政策"未申请"的声明与代码相符、
字数限内、三份文档互相不打架、图标尺寸与四角不透明、已知缺口如实记录。
## 没做的事(刻意的)
- 不生成截图:必须真机原生界面,不能是设计稿(审核明确禁止)
- 不填备案表:实名主体必须本人操作,且工信部短信核验要发到本人手机
- 不碰 AGC:需要登录用户账号,部分环节要扫码/人脸
|
2026-11-01 09:56:04 +08:00 |
|
laoyang
|
96168811a3
|
Map Kit 地址联想:module.json5 补 client_id metadata 占位 (laoyang/FamilyBook#33)
官方「开发准备 - Map Kit」要求 entry 模块的 module.json5 里有
metadata: { name: "client_id", value: <AGC 应用 Client ID> }。
只在 AGC 打开地图服务开关、不配这里,仍会报
1002600004 The Map permission is not enabled(错误码表"可能原因"第 2 条就是它)。
当前 value 是占位 REPLACE_WITH_AGC_CLIENT_ID —— 拿到 AGC 的 Client ID 后替换。
占位期间不会崩(认证失败 ⇒ fbGeoTips 返回空 ⇒ 输入框退化成普通文本框)。
验证:BUILD SUCCESSFUL(33 tasks,27 executed);
产物 entry-default-signed.hap 的 module.json 里 metadata 实测
[{"name":"client_id","value":"REPLACE_WITH_AGC_CLIENT_ID"}]。
|
2026-11-01 09:39:47 +08:00 |
|
laoyang
|
5661312679
|
日程编辑:确认「开始」后结束时间自动 = 开始 + 1 小时 (laoyang/FamilyBook#34)
用户 2026-11-01:
「当我选择了开始时间并确定之后,结束时间要跟着开始时间变成开始时间加1个小时。
也就是说,所有时间我们都默认它是一个小时。如果不是一个小时,再让他自己去改。」
## 改前
`applyPicker()` 只在**会变成负时长**时才动结束时间:
if (this.fEndDate < iso || (this.fEndDate === iso && this.fEndTime < hm)) { ... }
⇒ 用户改了开始、结束还挂在老位置,实际时长变成随机的。
## 改后
点「开始」确定 ⇒ 结束**无条件**重置为「开始 + 1 小时」。
改「结束」那条路一个字不动(改结束不影响开始)—— 这是「默认一小时、
不像一小时再自己改」的另一半。
全天日程跳过这段:时刻部分本来就不生效,改了不会落到服务端,
但会让"切回非全天"时看到一个莫名的时间。
## 新增 fbPlusHours()
`FbDateTimeField.ets` 里加 `fbPlusHours(date, time, hours)`,返回 `{date, time}`。
**不复用现成的 `fbAddHour()`**,因为那个有两个问题:
return fbPad2(Math.min(hh + 1, 23)) + ':' + mm;
① `Math.min(hh+1, 23)` **封顶 23 点** ⇒ 23:30 加一小时还是 23:30;
② 只返回时间、不带日期 ⇒ 表达不了「次日 00:30」。
跨天/跨月/跨年全部交给 `Date` 运算,不自己算天数
(自己 `if (hh>=24) dd+=1` 在"11 月 30 日 + 1 天"上就会错成 11 月 31 日)。
## 验证
**纯函数真值表 13 项**(从真实 `.ets` 抽函数、只擦类型标注后 node 直接跑,
不手抄替身;擦除后断言行数不变、无类型残留、没引入新字符):
普通 / 跨天整点 / 跨天半点 / 跨月 / 跨年 / 非闰年 2 月末 / **闰年 2 月末 ⇒ 2/29** / 午夜 / 带分钟
—— 全过。其中 23:30+1h ⇒ **次日 00:30**、12/31 ⇒ 次年 01/01、2028-02-28 ⇒ 2028-02-29。
**反向验证 3 项**(证明判据不是恒真):
坏A(封顶 23 点 / 不带日期)被抓到、坏B(丢分钟)被抓到、坏C(固定 28 天)被抓到。
**接线断言 12 项 + 反向验证 5 项**(静态,因为"函数对 ≠ 接进去了",
上一轮 #34 主体 bug 就是参数名对不上、编译运行都不报错):
- 开始分支调用 `fbPlusHours(iso, hm, 1)`、结果**同时**写 `fEndDate` 与 `fEndTime`
- 结束分支**没有任何** `fStart*` 赋值
- 全天日程有 `!this.fAllDay` 判断
- `pickIsEnd = !isStart`(#34 主体防回归)
- 不再残留 `this.fEndDate < iso` 那种条件顺延
- 反向:改回老写法 / 只写日期不写时间 / 改结束顺手改开始 / `pickIsEnd` 再反过来
→ 逐条转红;**对照组**(只改一句注释)仍绿 —— 证明断言不会"见到改动就红"
> ⚠️ 接线判据自己第一版有 bug,是反向验证抓出来的:坏1 用正则
> `if \(!this\.fAllDay\) \{[\s\S]*?\n \}` 找替换目标,但渲染层还有一个
> `if (!this.fAllDay)`(控制「结束」那一行显示),正则先吃到那个 ⇒ 注入落在无关位置
> ⇒ 断言仍绿(假阴性)。改成"先把 applyPicker 的函数体切出来再替换"。
**编译**:BUILD SUCCESSFUL,33 tasks(17 executed)。验签通过。
**⚠️ 已知存量失败(与本次无关)**:`_fb_arkts_guard.js` 跑出 1321 通过 / 3 失败,
三条都在工作区里**别的会话未提交的改动**上(`AGENDA_BACK_DAYS` 被换成 `CAL_FULL_FROM/TO`
导致"列表视图显式传日期范围"判据失配、`canEdit` 相关两条属 #31)。
本次只改 `FbDateTimeField.ets` 与 `FbEventEditSheet.ets`,不碰那些文件;
用 `git show HEAD:` 核实过 HEAD 版本里 `AGENDA_BACK_DAYS` 还在(判据本来通过)。
|
2026-11-01 09:38:14 +08:00 |
|
laoyang
|
d2d0a435a3
|
日程编辑:点「开始」却改了结束时间 laoyang/FamilyBook#34
用户 2026-10-30:「鸿蒙端,日程添加时,选择时间,点上面,
修改的反而是结束时间,这个是 bug。」
根因:openPicker 的参数语义与调用方相反。
· 调用方 dateTimeRow(label, isStart) 传的是 **isStart**
· 被调用方签名却是 openPicker(isEnd),内部直接 pickIsEnd = isEnd
⇒ isStart=true(点开始)→ isEnd=true → 整条链路按"结束"处理。
错得很"自洽"所以一直没被发现:
· 弹层初值读的是结束时间
· 确认后写回的也是结束时间
· 连标题都显示「选择结束时间」
三处一起错,"弹出来的值"和"改完的值"是一致的,看着不像坏。
修法:参数统一成 isStart(与调用方同名同义),内部自己取反。
没在调用处写 openPicker(!isStart) —— 双重否定更容易再错一次。
对照:观影表单 FbMovieNewSheet 用同一个 FbDateTimePicker 组件,
但 pickIsEnd 是显式赋 false/true,没有正反颠倒的空间,不受影响。
验证:
· 编译 BUILD SUCCESSFUL(33 tasks / 17 executed)
· 从真实源码提取三条映射做断言:pickIsEnd=!isStart、
读日期/读时间均在 isStart 时取 fStart* —— 全部通过
· 真值表:点开始→读fStart*→写fStart*→标题「选择开始时间」,
点结束→读fEnd*→写fEnd*→标题「选择结束时间」
· 反向验证:拿修复前的代码跑同一判据,正确转红
(旧版点开始时读写都是 fEnd*),证明判据不是恒真
· 全仓库排查 isStart/isEnd 用法,无同类问题
|
2026-10-30 15:41:38 +08:00 |
|
laoyang
|
7bc26dd7e1
|
地点联想:改用鸿蒙自带 Map Kit,不再走高德 laoyang/FamilyBook#33
用户指示「端侧不用高德,用鸿蒙自带的接口」。
改用 @kit.MapKit 的 site.queryAutoComplete(),顺带解决原方案两个问题:
① 高德 Web 服务 key 是服务端密钥,本不该出现在客户端;
② 线上那个 key 实际是 JS API 类型、调 Web 服务接口一直失败(见 #33 排查记录)。
现在端上不需要任何外部 key,也不需要我们自己的服务端接口与配额。
改动:
- FbGeoRepo:site.queryAutoComplete({query}) -> sites[]
取 name + formatAddress + location。
formatAddress 的 SDK 注释原文是 "Formatted detailed address of a place",
正是用户要的「作为参考的详细地址」。
formatAddress 缺失时用 locality + streetName + streetNumber 兜底。
- FbAddrInput:去掉 token 参数(MapKit 鉴权走 AGC 应用认证,与 API 令牌无关)。
- FbEventEditSheet / FbTripNodeSheet:两处调用同步去掉 token。
错误码全部降级为空结果、不打扰用户(声明文件列明的):
- 1002600003 App authentication failed
- 1002600004 The Map permission is not enabled
- 1002603001 Zero result(查不到,属正常业务结果,不刷错误日志)
- 1002600002 Failed to connect to the Map Kit server
联想挂掉 => 退化成普通文本框,手敲照常保存。
字段名一律以声明文件 @hms.core.map.site.d.ts 为准:
StreetNumber 的字段是 streetName / streetNumber,
第一版凭印象写成 street / number,编译直接报
"Property 'street' does not exist on type 'StreetNumber'",已修正。
编译通过(33 tasks,17 executed)。
|
2026-10-30 08:12:15 +08:00 |
|
laoyang
|
f4f27cea55
|
地点输入:补地址联想,下拉带详细地址好区分同名地点 laoyang/FamilyBook#36
用户 2026-10-30:「在所有调用高德显示详细地址的地方…不能只把地点告诉我,
一定要把详细地址也放在后面。因为同一个地址可能有好几个,
我根本不知道应该选哪一个。」
网页端(addr_suggest.js)已经是这个行为,端上原本**连联想都没有**(纯文本框)。
本次把能力补到端上:
- 新增 FbGeoRepo.ets:调服务端 /api/v1/geo/tips,最少 2 字才查
- 新增 FbAddrInput.ets:候选每行两行字(地名 14px 深色 + 详细地址 12px 灰),
同名地点靠第二行区分;防抖 260ms(与网页端同节奏);用 seq 丢弃过期响应
- 日程编辑、行程节点两处地点接入
🔴 走服务端代理而不是端上直连高德:高德 Web 服务 key 是服务端密钥,
打进安装包等于公开(反编译/抓包即得,配额会被跑光、key 可能被封)。
网页端用的 JS API key + 安全密钥是域名白名单那套,原生端没有等价机制。
⚠️ 必须是 @ComponentV2 组件而不是 @Builder:@Builder 的值类型参数
不参与状态观察(SDK r/quick-rules/05-state.md),候选列表/展开态写在
@Builder 里会数据变了界面不重建。本项目在 FbEventChip/FbTagChip/
FbCalOptionRow 上各踩过一次。
⚠️ 联想失败一律返回空候选 ⇒ 退化成普通文本框,手敲照常保存
(接口挂/没网/没配 key 都不能拖垮主流程)。
编译通过(33 tasks,17 executed)。
|
2026-10-30 08:03:13 +08:00 |
|
laoyang
|
f90cae0dcc
|
日程详情:只读日历不再给「编辑日程」入口 laoyang/FamilyBook#31
真机(DEL-AL20 / HarmonyOS 7.0.0.109)实测发现:只读共享本(如「工作学习」)
里的日程,首页详情卡仍显示「编辑日程」,用户改完点保存才被服务端拒:
「保存失败:这本日历你没有写权限。」
根因是 canEdit 写死 true,只判了 isEditableEvent(kind/id/calId),
没判这本日历能不能写。
- Types.ets 新增 isEventWritable(e, books):复用 FbCalBook.canWrite 口径
(由 FbCalRepo.fetchBooks 依 /calendar/books 的 kind+perm 算出),
不与写权限推导逻辑重复;books 缺失时判为不可编辑(宁可少给入口)
- FbHomePage:canEdit 由写死 true 改为按写权限判定;
openEvent 里顺手预加载 calBooks(原本懒加载,进详情时还是空数组,
会把本来能编辑的日程也误判成只读)
- FbCalendarPage:同步改用 isEventWritable,两处口径一致
真机复验:只读日程详情只剩「导航」;有权限的日程「编辑日程」正常,
改标题保存后杀进程重启仍在(确认真落库)。
|
2026-10-29 20:39:52 +08:00 |
|
laoyang
|
fd3ea53086
|
资源:点列表进只读详情,附件可预览/下载,编辑走底部按钮分离 laoyang/FamilyBook#29
用户原话(2026-10-28):「资源模块,点击资源后是编辑,那怎么下载资源
里面的文件呢?应该和名册一样,点击后是详情,属于谁、共享给谁、附件,
可以打开预览然后下载。下面有按钮可以编辑,点编辑之后才是编辑功能。」
原来 FbResourceSheet 是「查看=编辑」合体:点列表直接进可编辑框,附件区
点不动、没有预览/下载。这次拆成两级:
1) 新增 FbResourceDetailSheet(只读详情):标题/正文纯文本、归属人、
共享列表、附件列表(图片点开全屏预览、非图片点开交给系统「打开方式」、
每条附件可「下载」)。底部「编辑」按钮(仅 owner/write)→ 切编辑浮层。
2) FbResourcesPage 编排:点列表 → 详情;详情「编辑」→ 原 FbResourceSheet;
编辑保存/删除回列表。 + 新建仍直接进编辑浮层。
3) 新增 common/FbAttachOpen.ets:附件「下载明文 / 打开发 / 存相册 / 另存为」
的端上工具。走服务端 media.php 的移动端出口(能力早已就绪,纯端上改动)。
· 存相册:photoAccessHelper.showAssetsCreationDialog(系统弹窗,无需 WRITE 权限)
· 另存为:picker.DocumentViewPicker.save
· 系统打开:隐式 Want viewData + 读授权 flag
编译 BUILD SUCCESSFUL;守卫 _fb_arkts_guard.js 1324/0(浮层计数清单已同步
resources 1→3 模板、books 3→4 补 #28 存量、cardN 42→44)。
|
2026-10-28 23:53:53 +08:00 |
|
laoyang
|
f5a979f5c8
|
读书上传+在线阅读+笔记导入:端上补齐三个入口 laoyang/FamilyBook#28
用户原话(2026-10-28):「鸿蒙端对齐网页端」。读书端上原来只有书架。
1) FbBookUploadSheet:上传电子书(选文件→服务端解析建书)+ 粘贴导入读书笔记
2) FbBookReadSheet:在线阅读(章节列表 + 单章正文,一次一章)
3) FbBookSheet 详情态加「在线阅读」入口(有章节时才显示);
FbBooksPage 加「+上传/导入」入口 + 设置菜单「导入笔记」接上
编译 BUILD SUCCESSFUL;服务端冒烟 _i28_book_api.py 11/0 + 反证命中。
|
2026-10-28 22:47:38 +08:00 |
|
laoyang
|
f1f279f973
|
电影影评+评论:详情浮层接「评价与评论」真功能 laoyang/FamilyBook#27
用户原话(2026-10-28):「鸿蒙端对齐网页端」。观影详情原来只能看平均分。
现在:
1) FbMovieRepo 加影评/评论类型 + reviews/saveReview/deleteReview/
addReviewComment/deleteReviewComment 五个 API + 解析(含感受目录、评论树、mine 判断)
2) 新组件 FbMovieReviewSheet:我的影评(打分 1-10 星 + 观后感受多选 + 正文 +
改/删)、全部影评列表(含评论树、回复、逐条删除)
3) FbMovieDetailSheet 加「评价与评论」入口 + 内嵌影评浮层
⚠️ 影评数/均分仍以列表项为准(详情接口不返回,与既有注释一致)。
编译 BUILD SUCCESSFUL;服务端冒烟 _i27_movie_review_api.py 22/0 + 反证命中。
|
2026-10-28 22:30:09 +08:00 |
|
laoyang
|
59a9d3df39
|
任务共享:面板补「共享给谁」区块 laoyang/FamilyBook#26
用户原话(2026-10-28):「鸿蒙端对齐网页端」。任务面板原来没有共享入口,现在:
1) FbTaskShare 类型 + parseTaskDetail 解析 Task::find 已带的 shares
2) FbTaskRepo 补 fetchTaskShares/shareTask/unshareTask(删除走 query)
3) FbTaskPanel 加「共享给谁」区块:列表(可改/只看)+ 逐条取消 +
「+ 共享」复用选人组件(与资源/财务共享同一套)
编译 BUILD SUCCESSFUL;服务端冒烟 _i26_task_share_api.py 13/0 + 反证命中。
|
2026-10-28 22:10:15 +08:00 |
|
laoyang
|
f93dd92cdb
|
资源附件+共享:浮层补「附件」「共享」两个真功能 laoyang/FamilyBook#25
用户原话(2026-10-28):「鸿蒙端对齐网页端」。资源浮层原来「附件与共享
还在网页端,App 暂未开放」,现在服务端补齐 API 后:
1) FbResourceRepo 补 attach/share 写操作(上传走 fbUploadOne 字段 file;
删除走 query 传 attachment_id / person_id —— DELETE 带 JSON body 服务端读不到)
2) FbResourceSheet:附件区块(列表 + 缩略图/图标 + 「+传附件」+ 逐条删)+
共享区块(列表 + 「共享给…」复用选人组件 + 逐条取消)
· 只读共享来的不摆入口(与 canWrite 对齐)
编译 BUILD SUCCESSFUL;服务端冒烟 _i25_resource_api.py 21/0 + 反证命中。
|
2026-10-28 22:00:15 +08:00 |
|
laoyang
|
6f360fd6a1
|
财务折线:概览卡画净资产趋势(配服务端 /timeline)laoyang/FamilyBook#24
用户原话(2026-10-28):「鸿蒙端对齐网页端」。财务概览卡原来「折线图占位
(第二轮接图表能力后填)」,现在:
1) FbLedgerPoint 类型 + parseLedgerTimeline(读 /timeline 的 items)
2) loadEntries 里顺带拉 timeline(单独缓存键 ledger_timeline_<id>)
3) timelinePath():时序点 → 绝对坐标折线 path(Path 组件画,Y 轴按
min..max 归一,不是从 0 起 —— 金额平稳时不贴底);<2 点时给占位文案。
编译 BUILD SUCCESSFUL;守卫 _fb_arkts_guard.js 1324/0;服务端冒烟
_i24_timeline_api.py 8/0 + 反证命中。
|
2026-10-28 21:47:28 +08:00 |
|
laoyang
|
97846029c7
|
笔记图片块:展示真图 + 插图 + blocks 保存(图文混排后半程)laoyang/FamilyBook#23
用户原话(2026-10-28):「鸿蒙端对齐网页端」,笔记端上只支持纯文本。
这半程改鸿蒙端,配合服务端 cd56a9d 补的 /notes/upload:
1) 展示:FbNoteItem 新增 blocks(FbNoteBlock[]),parseNotes 把原始块
带进来;FbNoteSheet 里 image 块真画图(fbImageSrc),不再显示「[图片]」。
2) 插图:正文区下加「+ 插图」,选图 → fbUploadKey 上传拿 key → 加入
待保存列表;每张图带「×」可删。
3) 保存(P0 修复):带图时改走 saveNoteWithBlocks —— 正文 text 块 + 图片
image 块整组提交。原来只传 body,服务端折成单个 text 块整组替换,
**编辑带图笔记会把图片块静默删光**。
4) fbUploadKey 是 fbUpload 抽出的第二出口:复用同一 multipart 核心拿 key,
不复制第二份实现(本项目"两份实现漂移"栽过多次)。
编译 BUILD SUCCESSFUL;守卫 _fb_arkts_guard.js 1324/0;服务端冒烟
_i23_note_image_api.py 11/0(含 PATCH 后图片块不被删)+ 反证命中 2/2。
|
2026-10-28 21:35:11 +08:00 |
|
laoyang
|
3e910f1ca0
|
docs(card): 删掉「格子数与图数量永远相等」这句错误断言 #3
上一版(工单 #2)在这里写「这样"格子数"和"图数量"在结构上永远相等」。
这句话是错的,也正是工单 #3「21 格」的思想源头:
keys 按 poster **去重**收集、还卡 20 张上限
list 按 **行** 过滤、无上限,还会再裁偶数
两个集合基数不可能恒等,正确的说法只是「≤,不是 ==」。
把错误的断言留在注释里,下一个人会照着它推理,再踩一次同样的坑。
本次只改注释,无逻辑变更(守卫 1324/0、v97 反证 13/0 均通过)。
|
2026-10-28 10:49:20 +08:00 |
|
laoyang
|
acb2eadcb7
|
fix(card): 观影海报墙格数改偶数并按海报去重 #3
用户原话:「怎么是单数的?我这里看,显示的是21张图片。观影卡片。」
上一版(#2)修掉了「只有 6 张图」,但留下了新的结构问题:
· keys(配图)按 poster 去重,上限 20
· list(格子)按行过滤,无上限
⇒ 一部电影看了两场(两天各一场 / 重映),两行 poster 相同、拿到的 img
也相同,于是「有图的行数」会**多于**「图片张数」,可能是 21 这种单数。
两列栅格下奇数格必然让最后一行右侧留空 —— 就是用户看到的样子。
修法(`list` 的语义是「一部电影一格」,不是「一场观影一格」):
· 按 poster 去重(键用 poster 而非 title:同名重映 / 译名相同的不同片不算同一部)
· 顺带保证偶数:len - (len % 2),不写死 20/18(图少时减法会变负索引)
· poster === '' 的行显式跳过(没海报键的行不该进墙)
验证:静态守卫 1324/0(新增 groupV97 六条)· 反向验证 13/0(6 条注入各自命中)
编译 BUILD SUCCESSFUL,产物 6.2M。真机待验(hdc 设备掉线,见工单)。
顺带修掉两处「跑不起来」的环境问题(与本次 bug 无关,但会挡住验证):
· 编译脚本补 JAVA_HOME / PATH:PackageHap 阶段要 spawn java,
缺了只报 `Error Code: 00308018 Unknown Error`,真因
`spawn java ENOENT` 藏在 build-logs 深处。
· PATH 必须写 POSIX 风格 /d/... 而不是 D:/...:
MSYS2 的 PATH 是冒号分隔,`D:/xxx` 里的冒号会把路径劈成两段。
|
2026-10-28 10:10:33 +08:00 |
|
laoyang
|
f0d0ab9a05
|
fix(card): 观影 4x4/6x4 海报墙「只有 6 张图」修复 #2
用户原话:「观影4x4和4x6,只有六张图,划到第七张,就没图片了」
病根:两个上限各写各的
· CARD_IMG_MAX = 6 (配图数)
· rows.length < 30 (列表长度)
⇒ 前 6 格有海报,第 7 格起 img === '',posterCell 走 else 画淡紫占位块。
渲染没错、数据也没缺(线上 movie_details 221 条全有 poster_key,去重 205 张),
纯粹是「配图数 < 格子数」的结构性问题。
修法(不是把 6 改成 20 就完事,而是让格子数由图数决定):
· CARD_IMG_MAX 6 → 20,依据华为 Form Kit FAQ:API 20+ 上限 20 张 / 10MB,
本机 const.ohos.apiversion = 26 走这一档;
内存按解码后算 240×360×4×20 = 6.59MB < 10MB,留 3.4MB 余量。
· 拆出 CARD_ROW_MAX = 30(命名常量),专给不画图的 2x2/2x4 纯文本行。
· 新增 list:只保留 img !== '' 的行,长度天然 ≤ CARD_IMG_MAX
⇒ 格子数与图数在结构上永远相等,某张 404 也不会冒空格子。
· fbAttachPosters 签名 (recent, list) → (rows),两个数组各写一遍循环太容易只改一处。
· 新增 fbCardImagePaths 并发取图(MAX_PARALLEL = 4):20 张串行实测 10~17s,
卡片刷新窗口撑不住。MAX_FILES 24 必须 ≥ CARD_IMG_MAX,否则后下的挤掉先下的。
验证:静态守卫 1317/0;反向验证 15/0(7 条注入各自恰好命中目标断言)。
其中注入 A 抓出原先一条假绿断言:写的是「CARD_IMG_MAX >= 4」,
而 bug 原值正好是 6,6 >= 4 恒真 ⇒ 等于什么都没守。已改成「> 6」。
|
2026-10-27 23:06:24 +08:00 |
|
laoyang
|
b84ea06b73
|
v95 服务卡片四项:日程卡「今」按钮放大、观影/任务 2x4 列表可滑动、观影 4x4/6x4 补列间距且 6x4 改两列
#1
用户提四项(2026-10-26):
「日程的右上角那个今天的今,特别不好点,一不小心就到日程里面去了……
是不是可以把按钮弄的大一点。」
「观影的卡片,2x4,右侧观影的列表,不能上下滑动,第三个被截断,很难看,
如果显示多个,就要能滑动,如果不能滑动,就显示两个。」
「任务的 2x4 卡片,也是一样的。」
「观影的 4x4 卡片,和 4x6 卡片,两列图片挤在一起,中间没有空隙,应该缩小一点,
这个大小,我感觉应该根据屏幕的大小来。4x6,宽度都是一样的,为啥要弄成三列?」
① 日程卡「今」按钮 22x22 → 32x32
病根:22 的圆比最小建议点击区小一半,手指偏一点就落到卡片本体的
`.onClick(openApp)` 上 ⇒ 「想回今天」变成「进了日程详情」。
面积 484 → 1024(2.1 倍);同一份 sideBtn() 里的「+」一并放大,
翻页键 ‹ › 本来 32,现在四个按钮手感一致。
为什么是 32 不是 40:4x4 卡高度紧,顶栏与内容区竖向堆叠
(内容区 layoutWeight(1)),顶栏长高多少内容区就少多少。
必须改 width/height 本身 —— 卡片里这是 Button,可点区就是自己的宽高。
②③ 观影/任务 2x4 右半 Column → List(可滚动)
病根不在条数而在容器:Column 根本不滚动 + 硬截 3 条,
3x(38+10=48) + 2x6 = 156vp 而可用仅 ≈130vp ⇒ 第三条被压扁/裁掉。
条数上限 3 → MR_2X4_MAX=12 / TK_2X4_MAX=30;滚动条 BarState.Auto
(条目少时不露空滚动条)。数据侧 FbCardMediaData 两处 `rows.length < 4`
同步放到 30,否则卡上拿到的还是 4 条,改了容器也滑不出东西。
④ 观影 4x4 / 6x4 补列间距 + 6x4 三列改两列
病根:`List({ space: 8 })` 的 space 在 lanes 多列模式下只管行间距,
列与列之间不会自动留缝;海报格 .width('100%') + .aspectRatio(0.68)
又贴着 lane 边排 ⇒ 两列严丝合缝贴死。
改法:.lanes(2) → .lanes(2, 8),第二个位置参数才是列间距 gutter。
⚠️ gutter 是位置参数(SDK list.d.ts: lanes(value, gutter?: Dimension),
Dimension = PX|VP|FP|LPX|Percentage|Resource)⇒ 传数字,
写成 { gutter: 8 } 是类型错误(中途踩到,编译期才炸)。
6x4 同时从 .lanes(3) 改成两列,与 4x4 一致;文件头那句
「这块卡更宽、一排放三张」已过时,已注明别再照着它改回去。
同批带上 v92 未提交的观影海报墙实现(此前只在工作树里,从未提交过):
- 新增 data/FbCardImages.ets(下载 + desiredSize 缩图 + fd 管理)
- FbCardMediaData / FbFormAbility / FbCardForms 里的 formImages 管道
- string.json 卡片名带尺寸(「家书 · 日程 4×4」/「家书 · 观影 4×4」)
⚠️ FbCardImages.ets 已被 FbCardMediaData import,不一起提交就编译不过。
验证:
- 静态守卫 _fb_arkts_guard.js:1303 通过 / 0 失败
(新增 groupV95() 共 28 条,钉住按钮尺寸、List 容器、条数上限、
两列 + gutter>0、且不许写成 { gutter: N } 对象)
- 反向验证 _fb_v59d_falsify.py:289 通过 / 0 失败
(新增 9 条注入,逐条证明上述断言真的会红)
- 真编译 hvigorw assembleHap:BUILD SUCCESSFUL,HAP 产物已生成
⚠️ 真机验收未做:hdc list targets 为空(设备未连/未解锁)。
这四项全是观感与手感,静态断言证明不了「点得着、滑得动、看着舒服」,
待设备连上后补真机探针(重点:真的点「今」、真的滑列表、量两列实际间距)。
|
2026-10-26 07:28:06 +08:00 |
|
laoyang
|
7ae34e6e23
|
v91 观影/任务卡加 4x4/6x4(滚动列表+新建+卡上完成任务);修任务卡无数据(解析器用错:看板嵌套需 parseTasks)
|
2026-10-25 09:29:41 +08:00 |
|
laoyang
|
6711197a94
|
v90 新增观影卡与任务卡(2x2/2x4):卡片按类型分流推数据,key 带 version 前缀
|
2026-10-24 23:44:46 +08:00 |
|
laoyang
|
7ebe18b2d1
|
v89 修复服务卡片翻页后时间轴不重绘:ForEach key 加 version 前缀(key 撞车复用旧组件)
|
2026-10-24 21:53:13 +08:00 |
|
laoyang
|
d69beb629f
|
v88 旅程查看器全屏化:视频内联全屏播放、播放中直接滑动切换、只留底部计数
|
2026-10-24 21:53:05 +08:00 |
|
laoyang
|
c77370f521
|
v87 旅程查看器 Swiper 化:图片+视频混排翻页,滑动带动画
用户 2026-10-24:「图片和视频是不是可以都支持滑动啊?……滑动到视频
的时候,点击就直接播放。另外滑动的时候有没有个动画呀?现在就滑动,
整个一下就变化,特别生硬。」
实现:
- photoViewer 从「单张 Image + PanGesture(松手瞬跳 index)」改成
Swiper:跟手滑动 + 换页缓动动画(duration 350ms),loop 循环翻页
- 媒体列表改 mediaOf() = 图片在前视频在后(与缩略图墙同序),
viewerKeys:string[] → viewerPhotos:FbTripNodePhoto[](要带 mime)
- 视频也是翻页项:滑到视频页显示 ▶ + 文件名 +「点一下播放」,
点一下走 openVideo() 弹原有全屏播放器(系统控件不变)
- 底部 ‹ › 按钮走 viewerCtl.showPrevious()/showNext()(与手势同一条
动画通道;直接改 index 是瞬跳);计数由 onChange 统一更新
- 打开查看器用 changeIndex(at, false) 跳到所点那张,开局不播动画
- viewerStep()/imageKeysOf() 随 PanGesture 方案一并退场
真机实测(Mate X7,"2026国庆回老家"段附件 3 个媒体):
开查看器 1/3 → 左滑 2/3 → 3/3 视频页(点一下播放)→ 点击后视频
真实播放(5.mp4,00:05/00:29)→ 关播放器回查看器原位 → 按钮翻页
2/3→1/3 → 循环回绕 1/3→3/3 全部通过。
验证:守卫 1177/0(v87 组新增 10 条)· 反向验证全量 249/0
(v87 组 7 条全部命中)· 装机成功
|
2026-10-24 20:52:51 +08:00 |
|
laoyang
|
051d3974ba
|
v86 首页补齐五项入口(就地编辑 + 4 张卡新建 + 新增观影/联系人卡)
用户 2026-10-24「端上也全部都实现」,即 v84 验收时留下的三条:
名册新增成员 / 首页日程弹框后的编辑 / 首页其他卡片的新增。
实现:
- 日程详情卡「编辑」改成就地叠编辑表单,不再 closeEvent()+switchModule
- 近期任务 / 最近纪念日 / 最近观影 / 联系人 四张卡加「+ 新建」
- 观影卡与联系人卡端上原来没有,本次新建(数据走本地仓储,/home 不返回)
- backOnce 按层加 6 个分支;@Monitor 覆盖 8 个标志位
- FbSectionTitle 扩 addText/onAdd(带默认值,老调用点不改)
真机验收抓出三个「断言全绿但用不了」的 bug,一并修掉:
1. 编辑表单所有按钮点不动 —— Stack 层叠顺序写反,详情卡的整屏遮罩
盖在表单之上。dumpLayout 坐标全对、节点全在,只有真点才会发现。
2. 「+ 新建」点了没反应 —— syncNote 在本页只有赋值没有渲染点,
失败分支静默 return。统一改成 note() = 赋值 + 当场 Toast。
3. 改选「不重复」存完还是「每天」—— recognize 的保护逻辑(防清掉自定义
重复规则)误伤了「用户主动清空」:原规则非空 + 选 none ⇒ rrule 整项
不发。加 fRepeatTouched 区分「归入 none」与「主动选 none」。
验证:守卫 1167/0 · 反向验证全量 242/0 · 装机成功
端到端落库 建→改→删 全走通,测试数据已清理
|
2026-10-24 20:22:35 +08:00 |
|
laoyang
|
cdbf0a9593
|
v84: 首页「+ 新建」日程 + 红线去端点 + 新建表单两处可见性修复
用户 2026-10-23 原话:「日程,没有新建日程按钮,名册,也没有新增成员这个按钮。」
「首页上的日程,红色线不对,变短了左侧缺了一块。在日程模块中,那个红线左侧
是不是还有一个红点?如果有,去掉。」
① 首页日程区块加「+ 新建」(FbHomePage / Widgets.FbSectionTitle)
- FbSectionTitle 扩两个可选参数 addText / onAdd,带默认值 ⇒ 老调用点一行不改
- 按需懒拉 /calendar/books,缓存进 calBooks,同一会话只拉一次
- todayIso() 用本地年月日拼,**不用 toISOString**(UTC 会让早 8 点前算成昨天)
② 时间红线两端统一成「一根满宽线、无端点装饰、无行首文字」(FbDayTimeline)
- 老写法 `Row { Text(时间) width(34); 线; 圆点 }` ⇒ 行首 34vp 把线顶掉一块
(与网页端 .ag-now「双重缩进」同一类病),行尾 6x6 圆点就是用户问的红点
- 改成满高 Column + Blank 撑高 + 满宽 2px 线,与卡片端同一写法
③ 🔴 真机实测发现的两处可见性问题(FbEventEditSheet)
- 「加入哪本日历」原来排在「提醒」之后 ⇒ 表单高于卡片(maxHeight 86%),
它被推到滚动区下方,首屏只看到被底边切掉半行的标题(实测 bounds 高 7px)。
它是必选项(没选就「请选一本日历」存不上)⇒ 挪到「全天」之后、日期时间之前。
- 错误提示原来在滚动区**内部最底部** ⇒ 用户在顶部点「创建」时提示在几百 px
之下看不见;就算滚到底也被 Scroll 底边切半行。现象是「点了创建什么都没发生」。
⇒ 挪到滚动区外面、按钮区上方(实测:Scroll 底 1953,提示 1984~2024,按钮 2074)。
真机验收(设备 5NC0225B27004718,线上 family.yangyq.net):
- 静态守卫 1112/0;BUILD SUCCESSFUL;install bundle successfully
- 「+ 新建」bounds [868,931][1018,1045],点击真的弹出「新建日程」
- 诊断确认 books=15 canWrite=6(数据层完全正常,之前的「选不了日历本」是可见性问题)
- 日历本挪位后首屏可见:标题高 40px(原 7px),10 本 chip 从 838 排到 1358
- 错误提示挪位后不滚动即可见:命中「日程得有个标题」
- **端到端建卡成功**:线上库确认 id=2411 cal=5 20:00~21:00,首页 1 条→2 条;
测试数据已按 id 精确删除(remaining_test_rows=0),重启后恢复 1 条
|
2026-10-24 19:14:18 +08:00 |
|
laoyang
|
0bab0cdcc6
|
v83: 后台定期同步(四层调度,策略照 SyncCalendar)
用户原话:「另外,要有定期同步的功能,就是家书app在后台,也能自动同步
数据,这样打开app,基本上数据就是最新的。同步的策略,去synccalendar找,
里面有办法。」
四层调度(缺一层就会在某种场景下"永远不同步"):
1. 前台:onForeground 时若距上次同步超过 5 分钟,补一次
2. 退后台:requestSuspendDelay 短时任务(单次≤3分钟、单日10分钟配额)
3. 延迟任务:workScheduler(周期 2 小时 —— 系统按活跃分组压制,
请求更短不报错但不生效)
4. 桌面卡片:updateDuration:1(=30 分钟,唯一不受压制的通道,
每天上限 50 次);先推卡片再触发同步
关键约束(写错了都不报错,只是静默失效):
- WorkSchedulerExtensionAbility 单次回调上限 2 分钟,必须在 finally 里 stopWork
- 后台扩展跑在独立进程,EntryAbility.onCreate 的初始化一步都没跑过
⇒ prepare() 必须补齐 token store / module token / FbDb.open
- 回前台必须 cancelSuspendDelay 归还配额,否则两三天后用光
- workInfo 要带 isPersisted,否则重启后任务永久消失
- abilityName 必须与 module.json5 声明一致,对不上时 startWork 不报错但不回调
- ApplicationInfo 没有 bundleName 字段,包名在 .name
其他:
- 卡片驱动的同步有节流(CARD_MIN_GAP_MS 25 分钟 < 卡片刷新 30 分钟,
反了就是每次刷新都被跳过、一次都不跑)
- 设置页新增「同步」分组:开关 + 上次同步时间 + 说明
同时修:首页日程红线左侧缺一块(nowLayer 从 Row{圆点;线} 改成单个
Column 满宽 —— Row 里圆点占行首,线只能从第 5vp 起画)
守卫 1106/0、v83 反向验证 10/10 全中、已装机。
|
2026-10-24 04:25:15 +08:00 |
|
laoyang
|
1c6af8ac56
|
v79b 任务面板:补「看板」一行 + boardOf 反查兜底
真机验收时发现 boardOf() 定义了却从没被渲染,面板上看不出这条任务
属于哪个看板 —— 而「挪看板」正是 v79 新做的动作,做完看不见结果等于没做。
- infoRows() 增加 kvRow('看板', boardOf())
- boardOf() 在 base.board 为空时按 boardId 反查 boards,最后回落到「默认看板」
验证:静态守卫 1068/0(新增 ⑰ 三条)· 反向验证 222/0(新增 2 条注入)
真机探针 _fb_v79_verify.py 57/0
|
2026-10-22 18:46:04 +08:00 |
|
laoyang
|
559d79b2e2
|
v79b 端上:修 ForEach 键——改完状态卡片还显示旧状态
自查发现的隐患,**编译通过、静态断言全绿、节点也都在**,只是内容陈旧:
任务行是 `@Builder taskRow(t: FbTaskItem, …)` / `taskCard(t)` —— 入参按值传递、
不参与状态观察(本仓库文件头第 A 条反模式)。而 `ForEach` 的生成键只有
`'htk_' + t.id` ⇒ 改了状态 → 重拉列表 → `this.tasks` 换成新对象,但**键没变**
⇒ ArkUI 按 key 复用旧节点、不重跑生成器 ⇒ 卡片仍显示旧状态。
用户会看到:「面板里改成已完成了,关掉面板那张卡还是待办。」
修法:新增 `model/Types.taskKey(t)`,键里带**所有会被画到行上的字段**
(status / boardId / dueAt / overdue / childDone+childTotal / title),
首页与任务页两处 ForEach 都改用它;面板里子任务行的键也补上 `k.status`。
⚠️ 代价是状态一变这一行会重建(丢的是这一行的局部动画,不是滚动位置)。
这是刻意的取舍:**宁可重画,也不要不刷新**。
新增守卫 ⑯ 组 10 条(计数/关系型:
「键生成器必须调 taskKey」「taskKey 必须含这 6 个字段」「子任务键要带 status」),
并补 5 条反向验证注入(退回只拼 id / 漏 status / 漏 boardId / 子任务键缺 status)。
守卫 1064/0 · 反向 220/0 · 编译 BUILD SUCCESSFUL。
|
2026-10-22 17:33:32 +08:00 |
|
laoyang
|
25a0797a74
|
v79 端上:任务面板(首页/任务页共用)+ 任务模块功能补齐
用户原话:「鸿蒙端的功能,也需要同样适配,在首页上可以直接修改编辑任务,
另外任务模块的功能需要完善。」
端上原来只有"看列表 / 勾完成 / 新建改标题",网页 v78 那套「就地处理」
(改状态、挪分组、子任务、附件、留言、删除)一件都没有;首页点任务
弹的还是 v73 的只读卡。
一、新增 FbTaskPanel(首页与任务页共用同一组件)
· 改状态三段(待办/进行中/已完成)+ 标题左侧状态圈
· 挪看板 chips、勾/加子任务、加图片附件并删、发留言
· 编辑 → 交给宿主开 FbTaskEditSheet;删除要**点两次**(端上没有 undo)
· 每个动作成功后再 getTask() 重拉一次:一次动作会牵动子任务计数、
附件张数、看板色,逐处同步 DOM 必然漏一处
二、退役 FbTaskDetailSheet(v73 只读卡,387 行)
⚠️ 它已无人挂载,而守卫还在逐条"通过" —— 等于给死代码盖章。
三、任务模块补齐
· 卡片点击 → 面板(不再直接进编辑表单:误触一下就开始改标题,
且子任务/附件/留言没地方放)
· 「只看已完成」改真打 ?view=done:看板视图服务端就写死
status <> 'done',在客户端筛永远是空
· 看板列表改走 ?view=boards:原来从任务反推 ⇒ 空看板永远选不到
· 编辑表单补分类/循环两排
四、顺带修掉一个自查发现的真 bug
任务页里 sheetCount 有**两个** @Monitor 都监听 boardSheetOpen、都写同一个值
⇒ 关掉设置浮层时谁最后执行不确定,会把还开着的面板"顺便让位结束",
底部悬浮栏浮上来盖住面板按钮。已并成一个取并集的 monitor。
服务端(另一仓):新增 GET /tasks?view=boards、POST/DELETE /tasks/<id>/attach,
详情补 board_name/board_color_calc,越权删别条任务的附件 → 404。
验证:静态守卫 1055/0(新增 v79 一组,含"全页只许一个 sheetCount 赋值点")、
反向验证 215/0(新增 15 条注入,逐条证明新断言会红)、
服务端 e2e 1558/0、_fb_v79_api.py 26/0、编译通过并已装机。
|
2026-10-22 16:40:44 +08:00 |
|
laoyang
|
c7bd26dd1b
|
v77 旅程:列表只留整卡入口 + 动作区改成 2×2 + 地址不截断 + 题图
用户 2026-10-22 的五条:
① 列表卡去掉「看行程」「编辑」——「点进去之后会有编辑,直接点进去
看行程就可以」。卡尾整行删除,整卡可点变唯一入口(标题行尾留一个 ›
作提示);openEdit() 一并删掉,不留死代码。
② 详情页动作区重排——「排列得不好看,乱七八糟的」。旧版是四颗彩色实心
胶囊分两行、右端对不齐。现在 ☰ 挪到标题行右侧,四颗动作做成 2×2
等宽(layoutWeight(1));主操作保持实心,编辑/删除改浅底同色字。
配色与顺序仍是 v41 硬要求,一个 hex 都没改。
③ 出行方式:十种方式**不能增删**(网页版也只是只读 + "常用信息",
而端上加行程表单里根本没有车次/车牌,那条备忘在端上无从使用)
⇒ 方向反过来,给加行程表单补上「车次/航班号」(火车/飞机/轮船/
公交/地铁)和「车牌」(自驾,从车辆库选一台,服务端按 vehicle_id
join 出车牌)。没有这两项,"出行方式"在端上就只剩一个图标。
④ 行程详情里的地址显示不全——「从哪到哪,如果地址特别长就显示不全」。
原来出发地与到达地挤在一条 Row 里各 maxLines(1) + 省略号;
改成「从」「到」上下两行、整段铺开不截断。
⑤ 旅程题图:端上原来根本没有上传链路(服务端只有观影的 poster 槽位)。
服务端已加 POST /trips/<id>/cover;端上新建/编辑浮层加 16:9 题图选区,
流程「先建 → 拿 id → 再传图」,传图失败记 id 再点只补传(不会建出
两趟一样的旅程)。详情页空题图那块也能直接点开去加。
顺带把 multipart 上传收敛成一份(data/FbUpload.ets):原来头像 / 海报 /
观影照片各抄了一遍,旅程题图会是第四份。
|
2026-10-22 10:15:03 +08:00 |
|
laoyang
|
79e942d0e4
|
v76 旅程:信息铺得开 + 每段行程各自成卡 + 照片可点开 / 视频可播
用户 2026-10-22(四条毛病,同一个根因:信息往一行里挤):
「那个城市……如果一行显不下的话,应该显示在下边一行,把所有的城市全列出来。
就跟那个人员一样,你人员特别多的时候,一行显示不下,就直接往下放就行了。」
「点进详情之后……现在人员显示不下的话,你直接都放在右边,都溢出到这个块外面去了……
应该直接顺着往下铺就行了,所以它长一点无所谓。」
「实际行程它应该也是一段一段的,不要把它整个实际行程放在一块,
就是每一个行程都是一个一个块。然后里面的照片要可以点击查看,视频要能够播放。」
根因不是"忘了加滚动",而是 **`Row` 在 ArkUI 里不换行**:
塞不下的子节点会被压缩、或直接横向溢出父容器。四处都改成
`Flex({ wrap: FlexWrap.Wrap })`。
- `Types.ets`:`FbTripItem` 新增 `cities: string[]`(整串只能截断);
`FbTripNodePhoto` 新增 `mime` / `size`
- `FbParse.parseTrips` 新增 `cityListOf()`:优先用服务端 `cities` 数组,
只给整串时按 `·` 拆回来("显示不全"不再从别的入口漏进来)
- 列表卡:城市从「📍 一行长串 + 省略号」改成一颗颗 chip,日期单独占一行
- 详情页:城市 / 参与的人 / 每段的参与人 全部改 `Flex wrap`
(实测「黑龙江」那趟 5 个人自动排成 2 行,不再溢出卡片)
- **每段行程各自一张卡**:组头退成章节标题(不带卡片底),
`nodeCard` 自己带白底 + 圆角 + 左侧阶段色条(取 `n.stage`)
- 照片:点缩略图开全屏查看器(`PanGesture` 左右滑、上一张/下一张、关闭)
- 视频:按 `mime` 前缀认出视频(`is_image` 只有两态),缩略块 + ▶ 角标,
点开用系统 `Video` 播放(`media.php` 支持 Range,可拖进度条)
🔴 真机抓到的布局 bug:`Stack` 子节点上的 `.align(Alignment.Bottom/TopEnd)`
**完全无效** —— 关闭按钮和翻页条双双掉到屏幕正中。改成铺满的 Column +
`layoutWeight(1)` 撑开,并给控制层 `hitTestBehavior(Transparent)`
保证"点背景关闭"仍穿透。
验证:真机 33/0(含"城市 chip 数 == 统计里个城市"、"相邻行程卡有实际间隙"、
"点视频块起播放器且无放不出来");守卫 953/0;反向验证 181/0
(其中一条注入暴露了我新写断言的假绿,已改成结构化顺序判据)。
|
2026-10-22 09:02:16 +08:00 |
|
laoyang
|
8c92195558
|
v75b 详情页卡片溢出父容器(题图贴到屏幕左右边缘)
真机实测抓出来的:详情页题图 x=0..1080(满屏),而列表卡 x=50..1030。
图比下面的信息区宽 100px,左右各突 50px 贴到屏幕边缘。
根因:详情页每张卡写的是 `.width('100%')` + `.margin({left/right: pagePad})`。
ArkUI 里 `width('100%')` 量的是**父容器宽度**,margin 再往外挤 ⇒ 溢出父容器。
修法照列表页一直以来的写法:**父容器给 padding,卡片只写 width('100%')、
不写左右 margin**。改了三处:heroCard、nodeGroups 空态、stageGroup。
实测:详情题图 x=50..1030 (w=980),与所属卡片 50..1030 完全对齐。
探针 24/0,守卫 932/0,反向验证 165/0。
|
2026-10-22 08:07:05 +08:00 |
|
laoyang
|
3e2904cc3d
|
v75 旅程重构:列表页 + 详情整页,卡片改「图在上、信息在下」
用户 2026-10-21:
「每一个旅程里面的行程才是重头戏……点击一个旅程,应该进入到一个新的界面,
在里面列出所有的行程,就跟网页版的一样。
这个旅程的图片是比较重要的……图片在正上方,就是充满整个的这个列表的宽度。
然后呢把这个标题还有其他的一些信息放在下面。如果是图片放左边,信息放右边的话,
它总是被挤占,显示不全。……想看其他的这个旅程的话,直接往下滑就可以了。」
- 新建 `pages/trips/FbTripDetailPage.ets`(照 `trips/show.php`):题图 16:9 占满整宽、
五项统计、行程按 计划/实际/未成行 三组(空组不显示)、页头两行动作区
(+计划=蓝 / +实际=绿 / 编辑=黄 / 删除=红 / 设置)
- `FbTripsPage` 846→573 行:删掉就地展开全套,`tripCard` 改「题图在上、信息全在下面」
卡尾「看行程」自己带 onClick,整卡也可点
- `Types.ets` `FbTripNode` 8→18 字段 + 四个新类型;`FbTripRepo` 新增
`fetchTripDetail` / `fetchTripSummary` 与四个解析辅助
- 修真 bug:`parseNodes` 读 `expense_cent`,服务端写的是 `spent_cents`
⇒ 每段「💰 这一段花了」从来没显示过
- `Store.ets` 新增 `@Trace tripDetailId` 与 `leaveTo(parentKey)`:
删完整趟回列表要走它(内部 `applyModule` 会 `resetScroll`),
直接赋 `activeModule` 会让回列表后轻轻一滑就收起悬浮栏
- 接线:`Index` 分发 / `TopBar` 标题「旅程」/ `Modules.fbModuleColor` 归到 trips 色
- `FbTripNodeSheet` 新增 `@Param startStage` 预选阶段(选择器保留,点错还能改)
守卫 931/0,反向验证 164/0,编译 BUILD SUCCESSFUL,真机待设备连上后跑 _fb_v75_verify.py
|
2026-10-22 00:07:16 +08:00 |
|
laoyang
|
6e7cdaf5fc
|
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) 三参数,只给文本会静默不输入
- 空态文案是「换个关键词试试…」,按"没有/找到"判永远为假
|
2026-10-21 23:28:19 +08:00 |
|
laoyang
|
8cabc35400
|
v73b:家谱连线改成「每对父母一条母线」,去掉重合的横段
旧画法每个子女各画一条独立折线,在真实数据上会画出两条横段完全
重合的线(岳父母→我户 216→72 与 我父母→姐户 72→216,拐点同为
132.9),视觉上是一个「口」字框,看着像父母家和岳父母家连在一起。
新画法:父母单元中点 ──竖──→ 母线高度 ──横(只覆盖自己子女)──→
各子女 ──竖──→ 子女卡片上沿。一条线只服务一对父母,不再交叉重叠。
关键修正:子女挂点必须是**子女本人那张卡**,不是「子女所在户的中点」。
我这一户同时是两对父母的子女户(我父母→我、岳父母→妻子),
用户中点时两条竖线都落在 x=72、完全重合;改用本人卡的 x 后
妻子那条落在 105,两条线才分开。
⚠️ 这类病端点断言完全抓不到(端点都好好贴在卡片上),所以测试侧
新增「没有任何两条线段完全重合」,并在视图侧说明 bus 段是同一折线
公式的退化情形、不必单独写分支。
验证:离线 30/0、反向 13/0、真实数据+屏幕对照 11/0、
像素级 9/0、真机几何 15/0、ArkTS 守卫 905/0。
|
2026-10-21 22:57:56 +08:00 |
|
laoyang
|
13f716c936
|
v73:家谱自绘重做(Canvas 画线 + 原生卡片),去掉列表只留图谱
用户原话:「手机端这个家谱实在是很难看,能有什么库之类的,让它好看点吗」
改法:不再全 Canvas 手绘(只有彩色圆点 + 首字,中文又平又硬、做不出圆角阴影),
改成「Canvas 只画线 + 原生 Column/Text/FbAvatar 绝对定位」。
不能改用 Path 组件:Path.commands 单位是 px,而 .position() 是 vp,混用会缩 1/3;
Canvas 绘制坐标系本身是 vp,与 .position() 自洽。
布局算法抽成纯函数 common/KinLayout.ets(可单独验证,不再和"画"揉在一起):
- 横向单位是「夫妻单元」而不是「人」,夫妻并排紧邻
- 子女挂在夫妻单元中点下方(family-chart 语义)
- 迭代松弛 24 轮容纳冲突约束(家谱不是树:姻亲的"挂父母下方"与"夫妻并排"互相打架)
- 全程确定性(排序带 minId 兜底),避免每次重画卡片抖位置
真机抓到并修掉两个真 bug:
1. 户号 0 被当成"没分户":服务端 unit_of 从 0 编号,端上判 > 0
⇒ 我与妻子被拆两户(真机 Δx=680px)。修:布局层 >= 0,解析层缺失给 -1,
不把任何数值当哨兵。
2. 线没连对(用户原话「主要是线没连对」):buildBloodEdges 用 gen 数值当行号,
minGen=-1 ⇒ 长辈线从 y=-148 起(屏幕外),整组线串到上一代卡片。
修:新增 rowIndex(gens, gen),用走标换算而不是数值。
验证:
- 离线布局 29/0(含户号 0、脏数据一户三人、镜像常量与端上源码一致)
- 反向验证 11/0(9 处注入,含"户号 0 当没分户"与"用 gen 数值当行号"两条真 bug 复现)
- 真机几何 15/0、真实数据+屏幕对照 9/0(反解密度 3.125x,残差 0.23/0.32px;
12 个血缘线端点全部贴在卡片上下沿)
- ArkTS 静态守卫 905/0
|
2026-10-21 22:18:43 +08:00 |
|
laoyang
|
67ce443915
|
v73b:给卡片刷新加一条可观测的日志(排障用,不改变行为)
真机排障时发现:修复前 pushAll 在注册表为空时是**静默 return**,
hilog 里一个字符都抓不到 ⇒ 出现"卡片不刷新"时只能一路读代码反推
(v73 的跨进程 preferences 根因就死在那行 return 上)。
现在留两条:
· 为空 → warn,明确说"注册表为空(没卡片 或 跨进程没读到)"
· 推完 → info,报"已推 N 张卡(M 种偏移)"
真机实测:勾一次任务后日志出现
pushAll:已推 4 张卡(1 种偏移)
⇒ 应用进程确实读到了卡片进程写下的 4 个 formId。
|
2026-10-21 20:13:28 +08:00 |
|
laoyang
|
48a0507455
|
v73:桌面卡片刷新修复 + 首页日程/任务就地弹详情卡 + 一键导航
用户 2026-10-21 三条:
「卡片里面的数据并不刷新……应用里面的日程已经有了,但卡片上的日程却并不刷新」
「首页上的那个日程和任务,点击之后都应该像日程和任务里面的卡片一样弹出来,
不要进入到日程里面……有地址的还可以直接一键导航」
「任务也是一样,点击任务之后弹出来详情,可以标记它是不是完成了」
① 卡片不刷新(根因)
卡片进程(FormExtensionAbility)与应用进程靠 preferences 传 formIds / 翻页偏移。
SDK 明文禁止跨进程用 preferences,且 getPreferences() 在**进程内**缓存实例、
同名再取不读盘 ⇒ 应用进程可能永久持有空注册表 ⇒ pushAll() 首行
`if (list.length === 0) return` 直接返回 ⇒ 卡片只靠系统 30 分钟自刷。
修:store() 每次先 removePreferencesFromCacheSync 再取;并写死单写者纪律
(register/unregister/setOffset/clearOffset 只由卡片进程调,应用进程只读)。
② 首页点日程 → 弹 FbEventSheet(canEdit: false,首页不给编辑入口)
有地址的行整行可点 + 右侧「➤ 导航」胶囊;「编辑日程」才跳日历模块
(编辑是日历页的职责,不在首页复制第二份表单)。
③ 首页点任务 → 新的 FbTaskDetailSheet
状态圆可点(勾完成)、标题带删除线、底部「标记为已完成/未完成」+「改一改」。
状态**必须走 POST /tasks/<id>/status**(PATCH 会忽略 status)。
④ 一键导航 FbNav.ets:startAbility 隐式 Want(不是 openLink,那是 App Linking);
canOpenLink 需 module.json5 声明 querySchemes(未声明恒 false 且不抛错);
高德/百度/花瓣/腾讯按序探测,一个地图都没装则复制地点到剪贴板 + Toast。
⑤ 首页登记 FB_BACK(与其余 10 个模块一致),根容器 Stack(浮层是兄弟节点,
不能当 Column 子项排,否则卡片被挤出屏幕)。
静态守卫 905/0、v73 反向验证 17/0、assembleHap 通过、已装机启动。
|
2026-10-21 20:01:37 +08:00 |
|
laoyang
|
15bc3c8599
|
v69b:大卡抬头「星期 + 农历」合成一行(真机上第二行被 Divider 切掉半截)
真机截图看到 4×4 卡的抬头是「10月20日」下面压着半截字(看着像"一十三"):
原来是 12sp 日期 + 9sp 农历**两行**,4×4 卡高度紧,
第二行被下面的 Divider 压掉了一半。
⇒ 四处统一走 `subLine()` 拼成一行(`星期二 十一`):
2×2 / 2×4 本来就是单行、真机上是正常的,大卡跟它们对齐。
守卫加 v66 ⑭(四块卡都必须走 `subLine()`,且不许退回"农历有值就吞掉星期"的写法),
反向验证加一条注入(把 4×4 改回 `Text(this.lunarText)` ⇒ 守卫变红)。
|
2026-10-20 22:09:51 +08:00 |
|
laoyang
|
719c68202b
|
v69:日程桌面卡片完全照 SyncCalendar 重做(四种尺寸 + 翻页 + 时间轴)
用户 2026-10-20:「跟日程有关的桌面服务卡片,你就完全按照 synccalendar 来做就行。」
上一版卡片是"首页概览"(今日日程 + 在做/待办/逾期 + 纪念日),
是照着首页数据自己设计的版式。这一批按用户要求换成 **SyncCalendar 那套日程卡**:
## 四块尺寸(与 SyncCalendar 一一对应)
- 2×2 日期 / 星期+农历 / 当天条数大数字 + +
- 2×4 左半同 2×2,右半「接下来两条」
- 4×4 `‹ 10月20日 / 九月初一 › [今|+]` + 当天日程(时间轴 / 列表)
- 6×4 同 4×4,更宽(小时 40vp)
## 翻页(这是"照 SyncCalendar"的核心)
- 卡片按钮走 `postCardAction({action:'message', params:{pageAction}})`,
`FbFormAbility.onFormEvent` 改偏移 → 重新取数 → 推回卡片;
- **每张卡一份偏移量**,落 `preferences`(`data/FbCardForms.ets`):
SyncCalendar 只放内存 Map,进程被回收就丢 ⇒ 系统每 30 分钟刷新一次,
会把用户翻到的"下周三"悄悄拽回今天。家书这边连 `updateDuration` 的定时刷新
也按各自偏移重建(`onUpdateForm` 先 `getOffset` 再 `push`)。
- 偏移夹取 ±366 天:message 是外部输入,算到离谱年份会 Invalid Date(白卡)。
## 点卡片要落到"卡片显示的那一天"
- 卡片 router 带 `openDate`(**毫秒字符串**,数字参数是 int32,13 位会溢出)
与 `widgetAction=add`(点的是+);
- `EntryAbility.applyWantModule` 解析后写 `FbAppState.calJumpIso/Create/Seq`;
- 日程页两条入口都接上:冷启动 `aboutToAppear`、热启动 `@Monitor('app.calJumpSeq')`
(应用开着时点卡片不会重建页面,只靠 aboutToAppear 会"点了没反应")。
## 数据
- 换接口:`/api/v1/calendar?from=<那天>&to=<那天>`(原来打 `/home`)。
`authProbe: true` + 显式带 token 的硬要求不变(后台 401 不能把用户踢下线)。
- 农历行(`kind=lunar`)抽成抬头那一行小字,其余衍生条目(节气/节假日)照常进列表。
- 时间轴复用 `common/FbTimeline`(与日程页同一套算法),
卡片侧只做"比例 → vp"的换算(`fbCardTlLines`,纯函数、可断言)。
- 显示方式(时间轴/列表)跟「日程设置」联动;今天才画当前时间红线。
- 应用内改完日程 → `FB_CARD_FORMS.pushAll()` 立刻刷新桌面卡片
(不必等 30 分钟;推失败自动注销该 formId)。
## 验证
- `assembleHap` BUILD SUCCESSFUL;已装机(5NC0225B27004718)。
- `_fb_arkts_guard.js` **856 / 0**(v66 组按新契约重写:四尺寸登记、
字段名与数据类配对、数组必须 JSON 字符串、翻页三动作、
`onUpdateForm` 按偏移重建、冷/热启动两条入口)。
- `_fb_v59d_falsify.py` **146 / 0**(新增 6 条注入:定时刷新丢偏移、
偏移不落盘、4×4 少「下一天」、「热启动不响应」、不读 openDate、
字段名拼错、去 authProbe)。
⚠️ 其中"热启动"那条第一次注入**打偏了**:它先命中了文档注释里那半句
`@Monitor('app.calJumpSeq')` ⇒ 真正的装饰器没被改,守卫照样绿。
已把字面量从注释里去掉(本项目"注释里别抄字面标签"的老坑)。
- 真机跳转 `_fb_v69_card_jump.py`:
· `openDate=今天+3天` + `widgetAction=add` ⇒ 日程页落在 **10月23日**,
且「新建日程」浮层打开、日期就是 10月23日(dump 里 26 个浮层节点);
· 只带 `openDate` ⇒ 同样落在 10月23日,**不**弹新建浮层。
|
2026-10-20 21:59:23 +08:00 |
|
laoyang
|
030a9c7fbb
|
v68 真机验收:修两处真机才暴露的问题(账本权限标签 / 查看行程点不动)
设备接上后第一次真机验收(5NC0225B27004718),25 条断言里抓到两个**真 bug**:
## ① 主人账本也挂一颗「可写」
账本切换条的判据是 `if (b.myPerm !== 'owner')`,而服务端 `Ledger::mine/find`
**从来不给 `owner`**(主人也是 `write`)⇒ 这个 if 恒成立,自己建的账本
也被贴上「可写」,看着像"只是别人共享给我的"。
v68 上一批已经把共享浮层改成 `isOwner`,但这处列表标签漏了 ——
同一批里 `myPerm` 的注释还写着 `owner/read/write`,属于照着错误注释抄。
⇒ 改 `if (!b.isOwner)`,并把那段过期注释纠正。
## ② 卡尾「查看行程」是一颗点不动的字
屏幕上明明写着「查看行程」,点它**什么都不发生** —— 因为展开逻辑只挂在
**卡片主体**的 onClick 上,卡尾这颗 Text 是纯文本,没有 onClick。
用户看不到"要点头像/标题",只会去点这三个字。
⇒ 展开逻辑抽成 `toggleExpand(trip)`,卡片主体与卡尾文字共用;
卡尾那颗补 onClick 并加内边距(fontTiny 的纯文字热区太小,真机不好点)。
## 验证
- 真机验收 `_fb_v68_verify.py`(v66~v68 三批):**25 通过 / 0 失败**
(改前 19/6 → 修完 25/0;A2、C2/C3/C4 由红转绿)
- `_fb_arkts_guard.js` → **832 / 0**(新增 v68 ③ 两条、v66 ⑫ 三条)
- `_fb_v59d_falsify.py` → **141 / 0**(新增 2 条注入,都能让守卫变红)
⚠️ 「查看行程」那条注入第一次写的是"往后 500 字符内找 .onClick",删掉 onClick
守卫**照样绿**(500 字符内还有「编辑」那颗)⇒ 改成按段切(Text → 下一个 Column())。
|
2026-10-20 21:35:38 +08:00 |
|
laoyang
|
2ff90519fa
|
v68:财务补齐(改一笔/删一笔/账本改名/共享给谁)
用户 2026-10-20:「还有这个财务的显示也有问题」——v65 修的是**显示口径**
(端上按正负号重加 vs 服务端负债取绝对值);这一批补的是**动作**:
设置里「共享给谁」「账本名字」一直是灰条,账目也改不了删不掉。
## 根因:不是端上没写,是服务端没有路由
`Ledger::rename / setShare / unshare / shares / updateEntry / removeEntry`
**一直都有**,`ApiHandler::ledger()` 却只开了「建账本 / 记一笔 / 删账本」
⇒ 端上只能把这两条画成"暂未开放"。
## 端上做了什么
- 点一行账目 → 同一个浮层进**编辑态**(类别 / 金额 / 名称 / 备注 +
删除二次确认)。`aboutToAppear` 里把 @Param 拷进 @Local,
点第二行才不会还是上一笔的内容。
- 设置「账本名字」→ 改名浮层;「共享给谁」→ 共享浮层(列表 + 选人 +
逐个人取消)。列表解析走 `{items:[…]}`(`FbApiResult.data` 是原始 JSON)。
- 三个浮层共用 `recountSheets()` 让悬浮栏让位(原来只有"记一笔"有)。
## 顺手修的两处「对着服务端抄错」
1. `FbLedgerBook.myPerm` 的注释写 `owner / read / write`,
而服务端**从来不给 `owner`**(主人也是 `write`)⇒ 端上"是不是我建的"永远 false。
现在用服务端新补的 `isOwner` 布尔。
2. `FbLedgerEntry` 没有 `note` 字段 ⇒ 编辑时备注框永远是空的。补上类型与解析。
## 顺带
- 撤掉名册家谱那颗「补关系」按钮:从来没有 `onTap`,服务端 `relations`
只有读接口、网页版也没这个功能 ⇒ 留个永远点不动的按钮只会被当成"坏了"。
## 验证
- `hvigorw assembleHap` → BUILD SUCCESSFUL
- `_fb_arkts_guard.js` → 827 / 0(新增 groupV68 29 条)
- `_fb_v59d_falsify.py` → 139 / 0(v68 新增 9 条注入全部变红)
- ⚠️ 设备仍断连(`hdc list targets` → `[Empty]`),真机未验收
|
2026-10-20 20:18:38 +08:00 |
|
laoyang
|
ec2cb14c4c
|
v67:读书模块补齐(书能点进去 / 加一本 / 读书笔记 / 统计)
用户 2026-10-20:「其他的功能也逐步完成……读书点不进去,也没法加书,
设置也没有任何的东西。」
## 先修三处「对着服务端抄错的口径」(都不报错,只是显示是错的)
1. 状态值:服务端 `Book::STATUS` 只有 wish/reading/done/paused,**没有 want**;
`Book::status()` 遇到不认识的值会静默改成 `wish`,而端上分组键写的 `want`
⇒ 书架里「想读」那一组**整组不显示**(页头还数着它,看着像书丢了)。
`paused`(搁置)同样没列 ⇒ 搁置的书也人间蒸发。
2. 评分:服务端 `rating` 是 0–10,端上当 0–5 用 ⇒ 8 分显示成 `8.0/5`。
现在解析时折半、写回时 ×2。
3. 进度:服务端 `progress` 是「读到第几章」,端上当百分比 ⇒ 读到第 30 章 = 30%。
现在按 `chapter_count` 换算,没解析过章节的书不画假进度。
## 补齐三个死掉的入口
- 书行整行可点 → `FbBookSheet`(状态 / 评分 / 章节进度 / 读书笔记 / 删除二次确认)
- 页头「+ 加一本」→ 同一浮层的新建态(书名 / 作者 / 状态)
- 设置「读书统计」→ 端上现算(藏书/在读/读完/想读/搁置/笔记条数);
另两条(导入笔记 / 微信读书)要文件选择与授权,仍标「网页端」,不摆假入口
## 服务端能力边界(照实做)
- `PATCH /books/<id>` 只认 status / rating ⇒ 详情里不给改书名/作者的输入框
- 章节进度只读(`progress` 由阅读器翻页写,接口没有回写入口)
- 读书笔记走真实接口 `GET/POST /books/<id>/notes`
## 验证
- `hvigorw assembleHap` → BUILD SUCCESSFUL
- `_fb_arkts_guard.js` → 797 / 0(新增 groupV67 28 条,含「端上分组键 ==
服务端枚举」的跨语言对照)
- `_fb_v59d_falsify.py` → 130 / 0(v67 新增 13 条注入全部变红)
- ⚠️ 设备仍断连(`hdc list targets` → `[Empty]`),真机未验收
|
2026-10-20 17:14:40 +08:00 |
|
laoyang
|
58f5509c1c
|
v66 续:旅程三项设置(车辆管理 / 出行方式 / 节点类型)
用户 2026-10-20:「旅程的……车辆管理、出行方式、节点设置,这些东西都还没完成。」
这三项的 `action` 一直是**空串**(点了没反应),是本次批量需求里最后一块。
三项的真实能力并不一样,按真实情况做,不摆假入口:
· **车辆管理**:`vehicles` 表 + `Vehicle::save/destroy` 早就有,缺的只是 API
⇒ 新增 `GET/POST/PATCH/DELETE /api/v1/vehicles`;端上列表 + 内嵌表单
(车型 / 车牌 / 自家·租的·借的 / 车主或租车公司 / 订单号 / 默认车 / 备注)
· **节点类型**:`node_types` 表 + `NodeType::save/destroy` 同理
⇒ 新增 `GET/POST/PATCH/DELETE /api/v1/nodetypes`;改名会**级联**到已有行程
(`trip_nodes.node_type` 存的是名字,服务端 `NodeType::save` 里做)
· 内置类型**不画删除按钮**(服务端会拒,画了就是"点了没反应")
· **出行方式**:**枚举常量**(`Trip::TRAVEL_MODES`),没有"管理"这回事
⇒ 只做一份只读对照表 + "加行程时选",**不做可编辑表单**
(反向验证里有一条专门锁"这个浮层里不许出现保存/删除")
几个实现上的要点:
· 两条新路由是**平级资源**(`/vehicles`、`/nodetypes`),不是 `trips/xxx` 子路由 ——
`Api::dispatch()` 只解析三段,写成四段会落到「trips 的第 0 条」= 列表
· 删不掉(还有 N 段行程在用 / 内置类型)回 **422 + 服务端原话**,不是 500:
那是"现在不能删",不是"服务器出错"
· 表单输入框**不按值传 value**:`@Builder` 的值参数不参与状态观察,
而这个浮层是"列表 + 内嵌表单"(点另一行的「编辑」时表单已经渲染着)
⇒ 会显示上一辆车的内容。改成传 key、builder 内部直接读 `this.fXxx`
· 三个浮层都遵守浮层铁律(根 `Stack` 叠放 + `hitTestBehavior(Transparent)`),
各挂一个 `@Monitor` 让底部悬浮栏让位;`backOnce()` 按层级顺序关
服务端(`app/ApiHandler.php`)已随 `familybook_push.sh` 部署,线上核对:
`/api/v1/ping` 的 features 里 `trips_vehicles` / `trips_nodetypes` 均为 1,
两个端点无令牌时回 **401 unauthorized**(说明路由存在,而不是 404 not_found)。
验证:`hvigorw assembleHap` BUILD SUCCESSFUL;静态守卫 769 条全绿;
反向验证 116 条注入全部能让守卫变红(含"字段名拼错""按值传 value"等假绿陷阱)。
⚠️ 设备仍断连,真机未验收。
|
2026-10-20 16:36:30 +08:00 |
|
laoyang
|
b3e1330244
|
v65/v66:六个空壳模块接线 + 桌面服务卡片
用户 2026-10-20:
「做几个和 syncCalendar 差不多的卡片。另外其他的功能也逐步完成……旅程的
新建旅程啊,车辆管理、出行方式、节点设置……资源也点不进去……任务也点不
进去,也没法完成……纪念日、新增纪念日……财务的显示也有问题……笔记现在
也点不进去,记笔记也记不了。」
v65 —— 六个模块(任务 / 资源 / 纪念日 / 笔记 / 账本 / 旅程)原本是**同一套
空壳模板**复制出来的:`FbPrimaryButton` 没有 `onTap`、`ListItem` 没有
`onClick`、设置菜单的 `action` 全是空串。这一版把它们全部接上真实接口:
· 任务:新建 / 编辑(`FbTaskEditSheet` + `FbTaskRepo`)、列表可点、勾完成
(走 `/tasks/<id>/status` —— 服务端 `PATCH` **显式忽略** status,走 Patch
会得到"看起来成功的假成功")、按看板筛选
· 资源:新增 / 查看 / 改 / 删(`FbResourceSheet` + `FbResourceRepo`);
过滤维度从**并不存在的**「证件 / 资料」改成按权限与归属
(`resources` 表没有 category 列,那个筛选一直是筛空的)
· 纪念日:新增 / 编辑(`FbDayEditSheet` + `FbDayRepo`);派生生日
(id=0、来自名册)只给提示不给编辑 —— 点开 `/days/0` 必然 404
· 笔记:记一条 / 打开 / 改(`FbNoteSheet` + `FbNoteRepo`),正文按 blocks 存
· 账本:记一笔(`FbLedgerEntrySheet` + `FbLedgerRepo`);修掉财务显示自相
矛盾 —— 资产/负债改回**服务端 summary**(原来端上按金额正负号重算,与
服务端"负债取绝对值"的口径打架,会算出 净资产 ≠ 资产 − 负债)
· 旅程:题图卡片(封面 + 标题 + 城市)+ 行程 + 新建旅程 + 新建节点
服务端同时修掉两处**静默数据丢失**(网页端因提交格式凑巧没暴露,只有走
API 的客户端会中招):
· 新建笔记传的是 `body`,而 `Note::create()` 只读 `blocks` ⇒ 新笔记只有标题
· 改笔记时 `blocks` 恒为空,而 `Note::update()` 是整组替换 ⇒ 只改标题就把
正文全删光;另修 `annivPeopleOf()`(`find()` 的 SQL 不含 people,原来那个
兜底恒为空 ⇒ 只改纪念日标题会清空关联人)
v66 —— 桌面服务卡片(用户选了「桌面服务卡片」这一项):
· `FbFormAbility`(FormExtensionAbility)+ `form_config.json` 三块尺寸:
2×2 今日概览 / 2×4 左右分栏 / 4×4 今日全景
· `FbCardData` 只打 `/home` 一个接口(与网页首页同源),数据**扁平化**后
由 `formBindingData` 推给卡片;断网时用上次成功的缓存并注明"离线"
· 卡片是后台进程 ⇒ 请求带 `authProbe: true`:401 **不能**触发全局"踢回
登录页"(那会顺手把正在用应用的人踢下线,v62 那次事故同一个坑)
· 点卡片 → `postCardAction(router, fbModule)` → `EntryAbility` 读 Want 参数
直接落到对应模块;补了 `onNewWant`(没有它,第二次点卡片"什么也没发生")
· 4×4 右上角 ↻ 走 `postAction(message)` → `onFormEvent` 重取数再推回
验证:`hvigorw assembleHap` BUILD SUCCESSFUL;打包后的 hap 里确认
`FbFormAbility`(type=form)与 `$profile:form_config` 都在,卡片代码进了
`ets/widgets.abc`。静态守卫 730 条全绿,反向验证 110 条注入全部能让守卫变红。
⚠️ 设备仍断连(`hdc list targets` 为空),真机尚未验收。
|
2026-10-20 15:27:32 +08:00 |
|
laoyang
|
3b7238a418
|
v64b:修真机弹层"点了没反应"(根容器 Column → Stack)+ 日期行热区
四个日期/选择类浮层在真机上**完全打不开**:编译无告警、`aboutToAppear` 照常调用、
`dumpLayout` 里连弹层一个节点都看不到、点它会"穿透"到底下的列表。
根因(逐层二分定位):根容器写成 `Column` 且里面**先排一个整高遮罩**
(`Column().width('100%').height('100%').backgroundColor(FB_MASK)`)再排卡片。
`Column` 是**线性**布局 ⇒ 遮罩占满整高后,卡片被排到屏幕**外面**。
⇒ 全屏遮罩与卡片要"叠"(`Stack`),不能"排"(`Column`)。
改动:
- FbDateTimeField / FbCinemaPickSheet / FbPersonPickSheet:根容器改
`Stack({ alignContent: Alignment.Center })`,遮罩与卡片作并列子项叠放;
去掉原先用于"居中卡片"的 `justifyContent(FlexAlign.Center)`(Stack 已对齐)。
- FbDateTimeField:「日期行」的 `.onClick` 由内层 `layoutWeight(1)` 的 Row
挪到**根 Row** —— 挂内层时那一行会被表单所在的**两列容器**盖住,
点击穿透到底下的观影列表(表现为"点日期行,列表反而响应了")。
- FbSheetClose:`onClose` 由 `@Param` 改 `@Event`(语义归位;实测 `@Param`
声明回调**并不会失效**,✕ 一直是能点的 —— 这不是本轮 bug 的根因,别混为一谈)。
验证:
- 构建 BUILD SUCCESSFUL;
- 静态守卫 644/0(新增 8 条:三个组件的 Stack 叠放 + 整高遮罩非空转 +
全项目无"根 Column 先排整高遮罩"坏形态 + 日期行热区在根 Row);
- 守卫反向验证 92/0(新增 6 条注入,含把根 Stack 改回 Column、删遮罩、
热区挪回内层,逐条证明断言真会红)。
|
2026-10-20 08:55:27 +08:00 |
|
laoyang
|
8146aab1a7
|
v63+v64:影院管理补齐(v63)与观影「记一场」豆瓣/弹框选择(v64)
⚠️ 本提交含**两批**改动(同一批文件里交织,无法按文件拆开):
· v63(影院管理与搜索/统计/年报拆成平级入口)—— 本会话之外的工作,
随本轮一并提交以免工作区长期留半成品;
· v64(本会话,用户 2026-10-19 六条)。
v64 六条:
① 片名可经豆瓣自动获取简介与海报(与网页端同一份服务端输出:
summary 全文 + 已落盘的 poster_key);简介**只填空白的**,不覆盖用户写过的。
联想 320ms 防抖(与网页端同值)。
② 开始/结束改成**与日程同一个**日期时间选择控件 —— 新抽 components/FbDateTimeField.ets
(行 + 弹层 + 一组换算函数),日程那边旧的自绘实现整段删掉改用它
(用户要的就是"两处一样")。
③ 影院、同看人改成**弹框选择**:
· FbCinemaPickSheet:顶部搜索框,匹配「地方 + 名称」,「不记录影院」恒在列表;
· FbPersonPickSheet:顶部搜索框,拼音首字母**子序列**匹配(yyq 打成 yq 也能搜到),
多选走"暂存 + 确定"两段式(点一个不关窗)。
两排横向胶囊/头像全删(用户:"这种方式太难找了")。
④ 允许上传**多张**图片:海报 1 张 + 观影照片可一次选 9 张、可多次追加、可逐张删。
⚠️ 顺序是**先建记录再传图**(建记录失败就不会留孤儿图);图失败**不挡记录**。
⑤ 删掉「地点」输入口(location 字段仍发,网页端与历史数据不受影响)。
⑥ 观影页搜索框改明显:白底 + 实色描边 + 图标放大到 18 并染主题色 +
占位文字提到 muted/fontBody + 右侧「筛选 ›」暗示(用户:"根本看不清")。
FbMovieRepo 新增 doubanSuggest / doubanFetch / uploadPoster / uploadPhoto
(multipart 手写,与头像上传同一形态,抽成共用的 uploadOne)。
验证:静态守卫 636/0 · 端上反向验证 87/0 · 接口 e2e 57/0 · 服务端反向验证 18/0 ·
本地全量 e2e 1521/0 · hvigor 编译成功并已装机。
⚠️ 真机验证脚本 _fb_v64_verify.py 已写好(六条全覆盖),因设备锁屏未能执行。
|
2026-10-20 07:35:37 +08:00 |
|
laoyang
|
4a063114c5
|
v62:修 P0 登录被踢 / 关闭按钮统一右上角 / 观影三处 / 影院管理补齐 / 名册三处
用户 2026-10-19 第二批八条(原话逐条对应):
═══ ⑨ P0:「现在还是不稳定,会退出,提示:这台设备已经被退出登录,请重新登录。」
真因**是我 v61 自己埋的**:「个人设置」页(`FbAccountPage`)里两处 `fbApi`
调用**取了 `token` 变量却没传进请求选项** ⇒ 没有 Authorization 头 ⇒ 服务端 401
⇒ `FbHttp` 的全局回调 `unauthorizedCb` **清掉本地令牌 + 踢回登录页**。
而本页在 `aboutToAppear` 里就调它 ⇒ **点一下右上角头像,App 就退出登录**。
⚠️ 我上一轮把同一现象归因成"残留旧令牌 / 设备会话被顶掉"(还去线上查了
`api_tokens` 表,一切正常)—— **那个结论是错的**,等于把 bug 归给了环境。
修法(两处调用 + **两道架构防线**,缺一不可):
① 修两处调用,把 token 传进去;
② `fbApi` 内部:**本地有令牌、调用方又没传时自动补上**
(大部分接口本来就要鉴权,补上是"纠正";`/login`、`/ping` 带了也无害);
③ **401 只在"确实带了令牌"时才算被踢** —— 否则"没带令牌"也会触发全局清令牌,
那正是这次事故的机制。
═══ ④「你这个按钮到底是在弹框的右上角关闭,还是在下面关闭……全放在右上角。」
选**右上角 ✕**(底部已被 编辑/保存/删除 占满,再塞关闭会堆三四个按钮,分不清主操作)。
新增 `components/FbSheetClose.ets`:
· `FbSheetClose` —— 32vp 圆形 ✕(热区 40+,小图标在手机上点不中);
· `FbSheetTitleBar` —— 左标题 + 右收尾(设置类给 `confirmText` 显示「完成」)。
9 个浮层全接;名册详情 / 日程详情 / 简要卡**底部不再有「关闭」**。
⚠️ 浅色底走 `fbAlpha`,**不能拼 `+ '14'`** —— ArkUI 八位色是 `#AARRGGBB`
(本项目曾因写 `#00000066` 导致遮罩**从来没显示过**)。
═══ ①「观影……弹出的框左上角有一条灰色的线,不知道是什么。」
那是浮层顶部的**抽屉把手**(36×4 的 `line2` 浅灰短线)。本意是"可下拉关掉"的提示,
但 ① 它落在左上角、旁边没有文字;② 这个浮层的关闭本来就不靠下拉。
⇒ 用户认不出它 = 它没起作用,**删掉**(不改成居中 —— 那只是把灰线挪个位置)。
═══ ②「然后这个观影人的头像并没有带进来。」
同看人的 `FbAvatar` **漏传 `imageUrl`** —— 服务端 `Movie::attendees()` 本来就
`SELECT p.avatar`、端上 `parseAttendees` 也解析进了 `a.avatar`,
纯粹是**渲染时忘了用它** ⇒ 有头像的人也退化成色块。
⚠️ 与名册详情 2026-10-15 那个是同一个 bug(当时是 `FbPersonSheet` 漏传)。
═══ ③「点击这个图片呢,不能左右滑动看所有的这个图片。」
全屏查看器原来**只有底部「上一张/下一张」文字按钮**。现在整张大图左右滑翻页。
⚠️ 用 `.gesture(PanGesture(Horizontal))` 而不是 `SwipeGesture`:
`SwipeGesture` 要"快速划动"才识别,在有滚动容器的页面容易被先吃掉
(日程页实测:`gesture`/`priorityGesture` 都不触发)。`PanGesture` 看**累计位移**。
⚠️ 判定要求 `|dx| >= 40 && |dx| > |dy|`,否则竖划会被误判成翻页。
═══ ⑤「影院管理……增加影院呢、修改影院呢、高级搜索、统计、年度报告,这些全部都应该补充进去。」
端上原来**只有一个只读列表**(连"新增"都没有)。补成四个页签:
· **影院**:新增 / 修改 / 删除(服务端 CRUD 早就有,端上一直没接)+ 删除二次确认;
· **统计**:总览(看过/影院/人数/人次)+ 按影院/按年/按月的条形图;
· **搜索**:片名 / 影院 / 起止日期(条件全空时不查,与网页 `hasCond` 同行为);
· **年报**:按年切换(第一场/最后一场、最常去的影院、一起看最多的人、每月分布)。
⚠️ **统计/年报必须联网**(`/movies/stats|report`):口径要与网页**完全一致**
(同一份 `MovieStats`)。本地库算出来的是"已同步的那部分",
少同步一次就少一部 ⇒ 用户会发现"网页 124 部、App 121 部"。
⇒ 断网时明说"需要联网",而不是给一个偏低的数字。
⚠️ 影院写操作后**立刻回写本地库**(`refreshCinemas`),否则刚建的那家要等下次同步
才出现,看着像失败;并用 `INSERT OR REPLACE` 而非"清空再插"
(中途失败会留下一个空表 = 用户的影院列表整个消失)。
═══ ⑥「名册搜索,搜索后,下面有一个白色的框,什么内容都没有,删除。」
那是列表**末尾给悬浮栏留高度的占位块**(`height(FB_DOCK_RESERVE)`),
底色是 `FbColors.surface`(**白**)而列表容器是 `paper`(**米**)——
列表内容一短(搜索只剩一两条)它就露出来,看着像"什么都没有的白框"。
⇒ 改底色与列表同色。**不能删**:删了最后一张卡片会被悬浮栏压住。
═══ ⑦「如果一个人没头像,在列表是有一个文字的头像的,那么点击后弹框,也应该有。」
`FbAvatar` 原来是 `if/else`:有 `imageUrl` 就**只画 Image**,
而图片**加载失败时 `Image` 什么都不显示**(只露出纯色底)⇒ 看着是个光板。
(`alt()` 只在**加载中**起作用,失败后是空白。)
⇒ 改成**叠加**:底色 + 文字头像**常驻**,图片有就叠在上面
(加载成功即盖住文字,失败则自动露出文字头像)。
⚠️ 之前注释说"图层叠会错位"——那是因为当时想靠 Image 的 intrinsic size 撑尺寸;
现在两层都显式给 `dia`,尺寸来源唯一,不存在错位。
═══ ⑧「现在名册的列表太空了,应该显示更多内容,例如工作单位、职务。」
列表卡片加一行「单位|职务」(独立成行,避免与标签/生日挤)。
⚠️ 用 `persons.org` / `persons.title` 而**不是** `person_jobs`:
列表读的是**本地库**,而本地库只存 `persons` 那一行
(`person_jobs` 是一对多,端上没同步那张表)⇒ 服务端加了字段也到不了列表。
线上实测 801 人里 192 人有单位、48 人有职务,列表立刻有内容。
验证:
· 编译 BUILD SUCCESSFUL
· ArkTS 守卫 536/0(新增整组 groupV62,含 P0 两道防线)
· 反向验证 51/0(新增 13 条注入,逐条确认能变红)
· Schema 列守卫 12/0
· **真机 22/0**(6 组,含照片横滑 `1/5 → 2/5`、四个页签、点头像不被踢)
|
2026-10-19 15:27:31 +08:00 |
|
laoyang
|
853b0e0b20
|
v61:顶栏标题=模块名 / 抽屉瘦身 + 头像进个人设置 / 删除按钮红底 / 浮层动画 / 日程卡片与手势
用户这一批九条,逐条对应:
1)「页面上左上角的标题,不再是家书了,而是切换到那个模块,就显示哪个模块的标题。
这样在页面内容部,就不再出现模块的名字标题了。」
→ TopBar 主标题改读 moduleName()(通知/设置/个人设置三个非模块路由也有名字);
观影/纪念日/读书/笔记/通知五处页内大标题删掉。
2)「按钮,也需要和主题色保持一致,出现文字的时候,例如观影模块,
在左上角出现的时候,颜色也需要和主题色保持一致。」
→ 标题色走 fbModuleColor(app.activeModule)(一处算,11 个页面不用各写)。
3)「弹框的外圈颜色,跟模块的主题色保持一致。」
→ 日程详情原来传 bookColor(日历本色)⇒ 改 accentColor();
7 个模块设置浮层补传 accent: this.accent。
4)「删除按钮全部用红色背景。」
→ 日程编辑/人员编辑从红描边改红底白字;详情「删除联系人」从浅红底改红底白字;
两处确认框统一 FbColors.red。
5)「做几个动画,例如,点击名册列表,应该有一个过渡动画出现弹框,
点击详情,框变大了,也应该有一个过渡动画,点编辑,也有动画。」
→ 新建 common/FbAnim.ets(fbMaskTransition / fbSheetTransition /
fbSheetGrowTransition / fbViewTransition),9 个页面共 17 遮罩 + 16 浮层挂上;
简要卡→详情用 0.96→1 的"继续长大"(0.94 会先缩一下)。
6)「左侧抽屉,把通知消息、个人设置、修改密码、系统设置,都去掉,
放在个人设置页,就是在点击右侧头像那里。消息通知不用放进去,
直接在头像旁边那里就可以了。」
→ 抽屉底部那四行整块删除(抽屉只剩品牌区 + 模块列表);
新建 pages/account/FbAccountPage.ets(整页,__account 分发),
点头像 switchModule('__account');改密码真打服务端新接口。
7)「日程……日期下面:12本日历可见,本月124条,这句话去掉。
点击日程弹出的框,编辑按钮也颜色也要修改。
全天日程,例如农历,上面显示无标题,读不到这条日程,这个有问题。」
→ 删统计行;编辑按钮从写死名册青 #4A7C82 改模块主题色;
真根因:衍生条目(农历/节气)的 detailFull **一直是 null**
(它们没有 /events/<id> 可拉,原来直接 return)⇒ 垫 shellOf(),
并把"整卡报错"改成"照常画内容 + 一行小字";
空标题兜底从「(无标题)」改成日历本名。
8)「再做几个卡片,先做日程的,这个参考 synccalendar。」
→ FbDayEventPanel.eventRow 照 SyncCalendar:左侧 4vp 圆头色条 + 标题 +
时间/全天 + 「↻ 重复」徽标 + 右端日历本名(限宽 30%、同色);
一行一张独立圆角卡(12)+ 描边,卡间 space 8,不再用 Divider 连成一片。
9)「日程,有很多手势,你参考 synccalendar。」
→ 月历上滑→周视图、周条下划→月视图、周条左右滑→换周。
真机实测两次踩空后定下写法:**竖向必须 parallelGesture(PanGesture)**
(外层是 Scroll,SwipeGesture 的"快速划动"会被它的内置滚动先吃掉,
gesture/priorityGesture 都不行);**横滑用 gesture(PanGesture)**
(同一节点两条方向不同的 parallelGesture 会互相压制);
方向按累计位移判、并要求 |dy| > |dx|。切视图走 animateTo + fbViewTransition。
验证:
· 编译 BUILD SUCCESSFUL
· ArkTS 守卫 493/0;Schema 列守卫 12/0;反向验证 39/0(新增 22 条注入逐条确认能变红)
· 服务端接口 e2e 20/0(独立副本 + 真 MariaDB,5 种错误路径 + 原令牌仍有效)
· 真机 _fb_v61_verify.py 21/0(6 组全过);像素采样确认浮层主题色环实测存在
|
2026-10-19 12:19:54 +08:00 |
|
laoyang
|
1a67247407
|
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 个浮层逐个真机截图确认。
|
2026-10-19 08:29:44 +08:00 |
|
laoyang
|
7858de27e3
|
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 个模块全报"抽屉里没找到"(像应用坏了,其实是探针过期)。
- 资源/笔记两页写死了早已不存在的条目(户口本/房产证/阳台改造清单)⇒ 改成
结构性文案(副标题 + 常驻筛选胶囊),不随用户数据变化而恒红。
|
2026-10-18 23:12:57 +08:00 |
|
laoyang
|
1c1a859464
|
修「名册卡片右侧贴屏幕」的真凶:width('100%') 与 margin 打架(不是 flexShrink)
真机实测纠正了我第一轮的误判。
现象(用户):「名册列表左侧有空隙了……怎么右侧不是呢?右侧还是紧贴屏幕。」
像素实测:左米色 52~72px、**右米色 0px**;布局树里卡片盒子 x = **50~1080**。
🔴 真根因:卡片**同时**写了
.width('100%')
.margin({ left: 16, right: 16 })
`100%` 是相对**父容器全宽**(= 屏宽 1080),margin 再把盒子往右推
⇒ 盒子变成 50~1080(左边留 50、右边 0)—— 正是用户看到的形态。
⇒ 去掉 `.width('100%')`,Row 按"父宽 − margin"自适应,两边都是 16。
真机复验:卡片盒子 x = 50~**1030**(左右各 50,对称);
像素扫描 y=860/880/1090/1310 全部 **左=右**。
⚠️ 我第一轮误判成"内容把卡片撑破",去加了 `flexShrink(1)` —— 那是**错的**:
`flexShrink` 防的是"长名字把同行的小标签挤没",与本 bug 无关。
真正的证据是**布局树里卡片自己的 x 是 50~1080**(不是子节点越界)。
⇒ 教训:**先量"卡片本身"的盒子,再怀疑子节点。**
(`flexShrink` 保留 —— 它本身是有价值的防护,但注释已改正,
不再宣称它是本 bug 的修法。)
验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 378/0;
_fb_v59c_falsify.py 16/0(本批新增第 16 项注入:把 width('100%') 加回卡片,
必须被抓住。该判据本身也反复调了三轮才既不恒红、又能抓住注入:
"别处也有" ⇒ 恒红;"取卡片尾部窗口" ⇒ 覆盖不到注入点;
"取 personRow 整段" ⇒ 段里天然含 4 处 width(3 处在注释里)⇒ 基线红;
最终 = **剥注释 + 只看卡片自己那一层**)。
|
2026-10-18 21:56:34 +08:00 |
|
laoyang
|
412d2ae2f7
|
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],那是"注入没打到点上"。
|
2026-10-18 21:47:18 +08:00 |
|
laoyang
|
9bf21cd853
|
修「点头像没反应」:FbPersonSheet 塞进 bindSheet 会被立刻关掉(宿主契约冲突)
真机验证时抓到 —— 三条里第 ① 条第一轮**完全没用**:
点头像什么都不出来。每次点击日志都出现一组:
AceSheet: radius is not correct type
AceDrag: Drag gesture has been canceled ← 浮层起来立刻被取消
⇒ sheet **确实创建了**,但被测量失败关掉;界面上看就是"点了没反应"。
(我第一轮据此误判成 "onClick 没接上",白查了一轮点击链路 —— 点击是好的。)
根因:FbPersonSheet 的根节点是
Column() { /* 92% 宽的圆角卡 */ }
.width('100%').height('100%').justifyContent(FlexAlign.Center)
—— "铺满全屏 + 内部居中",因为它的**设计宿主**是
"页面里叠一层遮罩 + Stack 居中"(名册页、日程页都这么用)。
而 bindSheet 要求内容**按自身高度自适应** ⇒
"内容要铺满 100% 高" vs "sheet 要按内容定高" 互相打架 ⇒ 判为无效、立刻关。
修法(最小侵入,老调用点一个字不改):
① 给 FbPersonSheet 加 `@Param embedded: boolean = false`:
.width(this.embedded ? 'auto' : '100%')
.height(this.embedded ? 'auto' : '100%')
.justifyContent(this.embedded ? FlexAlign.Start : FlexAlign.Center)
.padding(bottom 在 embedded 时去掉"贴底手势区留白"那 16)
⇒ 默认 false = 老行为;顶栏那处传 embedded: true。
② sheet 高度用 SheetSize.LARGE 而不是 FIT_CONTENT
(卡片自带 maxHeight:72%,FIT_CONTENT 下会跟它抢高度)。
③ 删掉外层 myDetailPanel 上的 constraintSize({maxHeight:'82%'})
—— 高度由 sheet 定,三方都设 = 三方打架。
真机复验(装机后立即点):
浮层完整弹出且用的是**接口那份完整数据**(含本地没有的字段):
「杨永全 · 副教授 · 家中排行第 2 个 · 生日 11-16 · 邮箱 i@yangyq.net ·
地址 青岛市 · 身份证 372****4856 · 其它邮箱 yangyq@ouc.edu.cn ·
网址 https://yangyq.net · 工作经历 中国海洋大学|副教授」+ [编辑][关闭]
客观判据:uitest dumpLayout 的布局树字节数 119207 → 162736。
验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 352/0(再增 5 条钉住
embedded / SheetSize.LARGE / 不铺满 / 组件有开关);_fb_v59b_falsify.py 11/0。
另修一条旧断言(L 组"不再占满宽"原来锚死 `width('92%')` 字面量,
放宽为接受条件式,但没退化成"文件里有 92% 就行")。
|
2026-10-18 21:22:43 +08:00 |
|
laoyang
|
f0f0d9047a
|
头像热区改用 40×40 容器撑(直接给 FbAvatar 加 width 会拉成椭圆)
自查发现的一个"顺手就踩"的坑:为了扩大点击热区(32 的头像手指容易点空),
本想给 `FbAvatar` 直接加 `.width(40).height(40)` ——
但 `FbAvatar` 的**根节点就是那个 `Image/Text`**(自己已设 `width(dia)`),
外层尺寸修饰会把它拉到 40×40,而圆角仍是 `dia/2 = 16`
⇒ **头像变成椭圆**,而且编译不报错。
⇒ 改成外面包一层 40×40 的 `Row`(居中),头像仍是正圆 32、热区是 40。
同时补一条守卫钉住它(`_fb_arkts_guard.js` 348/0):
断言"`Row{ FbAvatar{...} }` 之后跟着 `.width(40)`",并在 falsify 里加了
第 9 项注入(把容器撑宽改成直接给头像加 width)⇒ 反向验证 9/0。
验证:hvigor BUILD SUCCESSFUL;守卫 348/0;反向验证 9/0。
|
2026-10-18 21:00:53 +08:00 |
|
laoyang
|
2b0401052c
|
v0.8.2 头像点进我的详情 / 未读数接真实数据 / 名册圆角卡(容器要换成页面底色)
用户 2026-10-18 夜三条:
①「右上角,显示的应该是登录者的头像,点击头像进入我的详情页面。」
头像**本来就是登录者本人的**(v59 已接 session.me.avatarUrl);
真正缺的是**点击** —— TopBar 里那个 FbAvatar **根本没有 onClick**,
只有一句写着"点开账号菜单"的注释,而"账号菜单"这个东西在代码里并不存在。
⇒ 接 openMyDetail() → bindSheet 弹 FbPersonSheet(复用名册那个人详情组件)。
🔴 数据必须走 repo.byId(meId)(本次为它新增 byId),不能从名册列表那批里找:
**"我"常常不在自己的名册里**(名册是"我挑出来的那一小撮"),
从列表里查会**静默落空**、点了没反应 —— 最难查的那种失败。
⚠️ 三种状态都说清楚:加载中 / 拿到了 / 拿不到,绝不留空白浮层。
②「提醒按钮,明明没提醒,怎么还显示个 2?」
🔴 真根因:`@Trace unread: number = 2;` 是**写死的占位初值**,
而全工程只有一处给它赋值(通知页「全部已读」清 0)。
没进过通知页的人**永远看到 2**;进了通知页又"永远 0"(把真实未读也盖掉)
—— 两个方向都错。
⇒ ① 初值改 0(拿到真值前必须是"没有未读",不能凭空多出 2);
② 首页把 /home 的真实 unread 写进全局状态。
(服务端 Notification::unreadCount,与通知页同源;/home 本来就返回它、
FbParse 也早就解析了 —— 只是**没人往全局状态里写**。)
③「名册页面,不好看,太丑了,应该是每个人都是圆角矩形,往中间收一点,
距离左右都有点距离,每个人中间都有一点空隙。」
🔴🔴 真凶**不是卡片形状**(v51 就做了 borderRadius + margin)——
而是**列表容器也是纯白** ⇒ 白卡压白底 ⇒ 圆角与左右收边**全看不见**,
看上去就是一整块通栏白。这解释了"改过一轮还是丑":上次改的是卡片,
问题在"背景没有对比"。
⇒ 容器 + 外层兜底改 FbColors.paper(页面底色),卡片保持白;
圆角 radiusS(10) → 新增 radiusCard(14)(列表项专用档:16 显胖、10 显方);
左右各 pagePad(16)、卡间 10(原 8 偏挤)、行内边距 11→12。
验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 347/0(本批新增 18 条,
另**收紧 2 条**旧判据 —— M 组"圆角卡"那条原来锚"逐字相邻两行"、且**测错了对象**:
没查容器背景,所以对"白卡压白底"这个真实缺陷毫无把关能力);
_fb_v59b_falsify.py 8 项注入 ⇒ 8/0。
⚠️ 本轮反向验证头一轮漏了 3 条(都是"判据太宽"),已逐条收紧并记入 pitfalls:
· 删掉头像 onClick 后仍绿 —— openMyDetail 这个标识符在**方法定义**里也出现;
· 容器改回白底后仍绿 —— 本页有**两处** FbColors.paper(容器 / 兜底);
· /home unread 改成写死 2 后仍绿 —— Notification::unreadCount 在 PHP 里也有两处。
|
2026-10-18 20:58:40 +08:00 |
|
laoyang
|
77b494a274
|
修「名册永远是空的」:person_roster.phones 只在迁移里加过、没进建表语句
用户 2026-10-18:「名册现在没有任何数据啊,看看咋回事。」
真机日志(唯一直接证据):
E .../Config: table person_roster has no column named phones
(SQLite errno 2,名册整批 batchInsert 全部失败)
根因(两条安装路径的行为分叉):
`person_roster.phones` 当初**只加在 `FbDb.migrate()` 的 v3→v4 ALTER 里**,
**没进 `FbSchema.FB_SCHEMA_SQL` 的建表语句**。于是:
· 老库升级 → 走 migrate() → 执行 ALTER → 有 phones ✅
· 全新安装 → store.version 一上来就等于 FB_DB_VERSION(5)
⇒ migrate() 首行 `from >= FB_DB_VERSION` 直接 return
⇒ 那句 ALTER **永远不执行** → 没有 phones ❌
用户这次是**卸载重装**(前端流程要求),正好走的是坏的那条路。
为什么藏了这么久(两个"静默"叠加):
① `pageHeaderItem` 的空状态文案是「登录后会从服务器拉取你名册里的人」——
一个**已经登录**的人被提示去登录,完全猜不到真实原因;
② `syncInBackground()` 里 `repo.sync()` 的**异常没人接**:
异常从 async 函数冒出去,`bootstrap()` 之后没有任何 catch
⇒ 整个后台同步静默死掉,`syncNote` 还是 ''(看着像"同步成功")。
三处修复:
① `FB_SCHEMA_SQL` 的 person_roster 补上 `phones`(新装用户直接对);
② `FB_DB_VERSION` 5→6 + 新增"结构自愈"迁移:**主动探测列是否存在**
(`PRAGMA table_info`,不靠异常控制流 —— 否则会把库损坏之类的真原因
一起吞成"大概已经加过了"),缺则补 `ALTER`。这一条是给**已经装坏的库**
(version=5 但缺列)用的:它们的版本号已经到位,不会再进任何 `from < N`
分支,只能靠新版本号再捞一次。
③ `syncInBackground()` 包 try/catch,把异常落成**用户看得见的文案**
(「名册写入本地库失败:<原因>」)。纪律:后台任务的异常必须就地
变成一句话,不能指望"调用方会 catch"——调用方往往是 fire-and-forget。
真机验证(两条路径都跑了):
· 旧库自愈:`本地库迁移:v5 → v6` → `v6 迁移完成:补上 person_roster.phones`
→ `[5] 本地库就绪,版本=6`;随后名册页**数据全出来了**
(标签云 全部/大学同学 55/同事 40/实验室 23/合作伙伴/爸爸/家人;
人员 爸爸/妈妈/岳父/岳母/杨永全 等),缺列报错 0 条。
· 全新安装(清数据重来):`version=0` → 首装建表 22 条 → 直接 v6,
迁移无事可做(正确:建表已含全部列)。
新增守卫(防这类"新装 vs 升级分叉"复发):
`_fb_guard_schema_columns.js`(11 条)静态解析 `FB_SCHEMA_SQL` 的建表列
与 `migrate()` 的全部 `ALTER ... ADD COLUMN`,断言**每个 ALTER 加过的列
都必须在建表语句里**;另钉住 v6 存在性探测、版本号、后台同步 try/catch。
`_fb_schema_falsify.py` 6 项注入 ⇒ 6/0(每项都成功打红对应断言)。
已挂进流水线(run_guarded_suite 全量 b59b)。
顺带修 `_fb_v57_falsify.py` 的一处**过时注入锚点**:它注入的是
`allowSkip: this.app.lockReason === ''`,而该参数已按用户要求整个删除
⇒ 改成反方向注入「把 onSkip 后门加回来必须被抓」(12/0 通过)。
验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 328/0;b57 12/0;新守卫 11/0。
|
2026-10-18 20:40:30 +08:00 |
|
laoyang
|
f020a671c0
|
v0.8.1 界面六条:删离线入口 / 标语统一 / 最近纪念日 / 图标真上色 / 真头像 / 日程四修
用户 2026-10-18 六条(全部已在真机逐条验证):
① 登录页「先不登录 / 离线使用」彻底删除
用户:「把那些不登录、看离线数据之类的提示全部去掉。以后就不再拥有这样的
功能了,把这些功能也全部删除。」
⇒ 两个按钮 + onSkip + allowSkip 参数 + Index 里的 skip 分支一起删净。
这是产品决定(不是隐藏):该路径本身就是安全缺口,v58 上了整库加密后更要堵死。
② 标语统一为「一家人用岁月写成的一本书」
用户:「这也是我们"家书"这个名字的由来。所以你不要再乱换这个标语了,
只要是想出现标语或者副标题的地方,就用这样一句话。」
· 端上 FB_DEFAULT_SLOGAN 是唯一定义处(登录页副标题 + 抽屉回落都用它);
· 抽屉左上角原来是家庭名,现在显示**标语**;
· 三层优先级(个人 slogan → 家庭 motto → 应用默认口号)**在服务端算**
(Family::slogan,与网页端首页 heroSlogan 同一套),/login 与 /me 都返回。
用户还说:「如果这个用户自己有标语……这个地方就是用户自己自定义的标语。」
真机实测:登录者杨永全自己写了「修身,齐家」⇒ 抽屉就显示他那句。
③ 首页新增「最近纪念日」(放在近期任务之后、往年的今天之前)
用户:「未来最近的三个纪念日,包括每个人的生日,以及手动填写的各个纪念日。
以倒计时形式展示:距离哪个纪念日还有多少天,距离谁谁谁的生日还有多少天。」
· /home.recent_days = Anniversary::upcoming(fam,pid,370) 取前 3 ——
取满一年:窗口太小会让"刚过完生日"的家庭一个都取不到,模块直接空着;
· 数据源 all() = 派生生日 + 自建纪念日,正好覆盖用户说的两样;
· 倒计时:0 天显示「就是今天」(写「还有 0 天」读起来像已经过了)。
④ 悬浮栏图标也上色(用户:「除了下面的汉字,上面图标的颜色也变成跟汉字一样的颜色」)
🔴🔴 真根因两层,都是"无报错失效":
· 第一层:Image.fillColor() **只对 SVG 生效**,而本工程 30+ 图标**全是 PNG**
⇒ 全工程 24 处图标染色一直是空操作(文字走 fontColor 所以只有文字变了)。
· 第二层(真机才现形):改用 colorFilter 后,矩阵写成了"系数乘 RGB"
(R' = R_原 × 系数) —— 对白色图标成立,但本工程图标是**纯黑**,
黑 × 任何系数 = 仍是黑 ⇒ 真机上图标像素依旧是 (0,0,0)。
⇒ 最终形态:`fbTint` 用 **alpha 当载体**(R' = (r/255) × A_原):
系数是归一化约定(官方灰度示例系数和为 1,量纲确定);
形状完全由原图 alpha 决定 ⇒ 黑/白图标都成立,描边抗锯齿自然过渡。
真机像素实测:日程项图标与文字同为 (176,120,64)≈#B0763A;
未选中项图标 (44,39,35) 深灰(不再是纯黑)。
⚠️ 全工程 24 处 fillColor 一并改掉;FbIconButton.tint 类型改成 string,
让"误传 Resource"变成编译错误而不是又一个静默失效。
⑤ 顶栏:真实头像 + 拉开按钮间距
用户:「右上角的头像应该是真实的……现在提醒的按钮距离头像的按钮太近了。」
· /home 给 me_avatar、/login 给 avatar_url(avatar_url() 按线上存储驱动拼好);
· 三颗按钮(同步 / 铃铛 / 头像)统一用 FbMetric.gapTopActions = 8。
⑥ 日程四处
· 「日期下面有几个点」⇒ 月视图与周视图的圆点全部删除,
连 FbDot / dots() / count / dotColor 一起清(不留死代码);
· 「农历全部都是初一」⇒ 真根因是端上 lunarOf() **硬编码 return '初一'**,
而服务端 lunarRange() 本来就每天给真实农历(title)⇒ 改取 title;
· 「列表这个视图,今天的数据缺一条」⇒ 服务端 /calendar 的 from/to **默认只给本月**,
而列表视图要显示 -7 ~ +60 天 ⇒ 端上显式传范围
(loadModule 新增 pathOverride 参数);
· 「点击了某一天之后并不明显,应该把这一天的颜色变深,而今天用一个较浅的颜色」
⇒ 照 SyncCalendar:选中 = 深色实底白字、今天 = 浅色底。
真机实测:17 号(选中)bg=#FFB0763A,18 号(今天)bg=#FFF5F0EA。
另:FbKeyStore 补上一个真机抓到的 HUKS 坑 —— isKeyItemExist 在密钥不存在时
**会抛 12000011**(不是返回 false),首次运行必被误判成"本机不支持 HUKS"
⇒ 永久明文降级。改成把"不存在"当正常分支,且降级态每次启动重试一次
(把同一把 DEK 重新用 KEK 包起来,不换密钥以免重下全部附件)。
验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 328/0;
_fb_v59ui_falsify.py 19 项注入 23/0;_fb_home_api.py 60/0;
六条已在真机逐条取像素/布局树确认。
|
2026-10-18 20:14:40 +08:00 |
|
laoyang
|
2132ee42d2
|
v0.8.0 本地加密 / 被踢清数据 / 增量同步 / 附件队列 / 顶栏同步进度
用户 2026-10-18 两条:
「直接做 L3 锁界面 + 本地加密(HUKS,踢设备即吊销密钥):数据在但读不出,
重登后恢复密钥即可用。」
「右上角头像和提醒前面加同步进度…首次全量、之后增量;进来先展示本地数据,
一边同步一边更新;附件后台队列同步(名册头像 / 电影海报 / 观影照片 / 资源附件)。」
① 整库加密(FbSchema v4→v5,新增 module_sync / attach_cache 两张表)
RDB encrypt=true。加密属性一经创建不可修改 ⇒ openWithEncryption 三段式:
直接开 → 失败删库重建 → 再失败降级明文(degraded 字段,设置页如实显示,
不静默降级)。
② 两层密钥(FbKeyStore)
HUKS 里 KEK(fb_kek_v1,不可导出)包裹 32 字节 DEK;revoke() 删 KEK
⇒ 已落盘附件密文在数学上解不开,这就是"踢设备即吊销密钥"。
重新登录重新生成 DEK 并重下附件(不做服务端托管,为了省一次重下
把能解你本地数据的密钥放到服务端不值)。
会话属性里 PURPOSE 只出现一次:原来把含 ENCRYPT|DECRYPT 的生成属性直接
拿去开会话再 push 一个 PURPOSE,同一 tag 出现两次在 HUKS 里行为未定义
⇒ 拆成 kekBaseProps / kekGenProps / kekSessionProps。
③ AES-256-GCM(FbCrypto)
落盘 iv(12)|ct|tag(16);变换串必须带 padding 段(AES256|GCM|PKCS7,
缺了抛 801);解密从尾部切 tag。
④ 附件后台队列(FbAttachQueue)
attach_cache 台账(pending/done/fail,MAX_TRIES=3);密文放 filesDir、
明文临时放 cacheDir;扩展名白名单筛 key。
图片地址收成统一入口 fbImageSrc(key,token):本地有就用 file://,
没有才回网络 —— 收成一个函数才漏不掉(5 个页面全部改过来)。
⑤ 被踢 / 退出清数据
lockOut → 清令牌 → FB_ATTACH.wipe(第一步就是吊销 KEK)→ FbDb.wipe
(先关句柄再 deleteRdbStore,并发去重,open 会等它)→ 同步状态复位。
重新登录后重建密钥 + 库 + 附件索引,否则之后只读网络不写缓存且不报错。
⑥ 增量同步(FbSyncAll 重写)
先问 /api/v1/changes;指纹相同就跳过。本地没水位必须拉(否则老用户升级后
一个模块都不同步);24h 陈旧上限兜"改无 updated_at 表的已有行";
/changes 拿不到时判 ping status>0 —— 断网直接收工,不退回全量
(否则 12 个模块各等 15 秒)。
家谱也进了预同步列表(原来只在打开时拉,离线是一片空白);
总步数由 CACHE_MODULES.length 算出,不写死。
⑦ 删除传播
服务端是物理删除、没有墓碑 ⇒ 观影同步改成"先清后插"
(persons 跨模块共用,不清)。
⑧ 顶栏同步进度
头像与铃铛之间加三态按钮(转圈 / 红感叹号 / 灰对勾)+ bindSheet 面板:
进度条、「N 个模块没有变化」、附件行、失败清单、立即同步(防连点)。
状态中心 FbSyncState(@ObservedV2 + @Trace,AppStorageV2 单例);
界面直接读 @Trace 字段,不提供派生 getter。
验证:hvigor BUILD SUCCESSFUL;_fb_arkts_guard.js 290/0(本批新增 59 条);
_fb_v58_falsify.py 31 项注入 38/0;b57 反向验证 12/0(锚点跟上本批 logout 改动);
本地 e2e 1519/0;全量流水线 PUSH_RC=0。
|
2026-10-18 16:01:41 +08:00 |
|
laoyang
|
776c95ba0b
|
v0.7.0 接上真实服务器:登录 / 设备会话 / 全量同步与各模块真实数据
从「演示数据壳子」走到「真的能连家里的服务器用」。
登录(用户 2026-10-17「完整的登录功能」)
· 首次启动先填服务器地址 → /api/v1/ping 自报门户(认得出"这地址上真是家书")
· 手机号或用户名 + 密码 → 拿令牌;失败提示统一,不泄露账号是否存在
· FbDeviceStore:首次启动生成并持久化设备 UUID(三方应用拿不到 UDID),
设备名取机型(marketName),并上报 platform / app_version
设备会话(v57,用户 2026-10-17)
· 一把令牌 = 一台设备的一条会话;每次启动打一次 /me(有网就验)
· 两种锁分开:need_login(401/被踢 → 清令牌)与 offline_expired
(离线超 24h → 只锁界面、不清数据、联网自动恢复)——
混成一种就会违背用户要的"一直联网就不用每天输密码"
· 运行期任何请求 401 → 网络层全局回调立刻锁定跳登录页(FbHttp 的 authProbe
让登录/校验类请求跳过,避免"两个地方同时跳登录页")
· 退出登录:先清本地令牌(主防线),再尽力通知服务端
· 设置页显示版本号与「这台设备」
数据层
· FbDb / FbSchema / FbParse / 各 Repo(日程 / 名册 / 观影 / 模块)+ FbSyncAll
· 首页改为服务端 /home(农历/节气/纪念日/生日/天气/统计/往年的今天)
· 日程页用 FbDayEventPanel,与网页端显示方式/红线/并排三处一致
验证:ArkTS 静态守卫 231/0;hvigor 编译 BUILD SUCCESSFUL(entry-default-signed.hap)。
|
2026-10-18 03:36:04 +08:00 |
|
laoyang
|
7eaa641437
|
v0.6.0 图标改为开抽屉入口 + 新增底部悬浮快捷栏(照鸿蒙 6 悬浮导航规范)
用户 2026-10-12 三条要求。
## ① 点顶栏图标 = 开抽屉
用户:「把那个图标改成这个功能吧,就是当用户点击左上角那个图标的时候,把抽屉列出来」。
⇒ `TopBar` 的 logo 加 `.onClick { drawerOpen = true }`。
⚠️ 点击直接挂在 Image 上、**不套 Button** —— Button 自带最小尺寸与内边距,
会把图标挤离左边线(用户要"图标靠左")。
⇒ 开抽屉现有三个入口:点图标 / 应用内水平滑动 / 底部悬浮栏。
## ② 底部悬浮快捷栏 FbFloatDock(新增)
用户:「页面正下方设置几个浮动按钮…比如日程、名册、任务和笔记…
这样用户不用拉抽屉,就可以直接点击使用。
但一定要注意,使用浮动的方式,**不要固定在下面**,而是**浮动着的**。」
🔴 **形态照鸿蒙 6 官方设计规范的「悬浮导航(Float Navigation)」**:
「导航栏脱离底部边界,以**四周留白、圆角卡片**形态悬浮于内容之上」。
参考实现参数直接用上:`height(56) + margin({left:16,right:16,bottom:16})
+ borderRadius(28)`,材质用系统沉浸材质
`uiMaterial.ImmersiveMaterial({ style: ULTRA_THIN })`(设计规范的"超薄"档)。
⚠️ 用了 systemMaterial 就不再叠加 backgroundBlurStyle/backgroundColor ——
材质自带模糊+边框+阴影,叠加会双重模糊发灰。
⚠️ **摆法关键**:`Index` 的根容器从 `Column` 改成
`Stack({ alignContent: Bottom })` —— 写在 Column 里它就是"占一行的固定底栏"
(老样式);用 Stack 覆盖 ⇒ 内容从它下面穿过、它浮在上面。
(实测印证:栏体 y=2132..2307,而"我的家人"标题在 y=2336 仍可见 ⇒ 内容穿过去了。)
⚠️ 内容底部加 `FB_DOCK_RESERVE`(88vp) 预留,否则最后一屏内容会被悬浮栏压住。
12 个页面统一处理。
⚠️ 宽屏不显示(抽屉常驻,且屏宽用不上);抽屉展开时也不显示(避免浮层叠浮层)。
⚠️ 选中项用模块主题色高亮,用户一眼知道"我在哪"。
## 验证(真机,全部通过)
· 点顶栏图标 ⇒ 抽屉打开 ✓
· 点悬浮栏「任务」⇒ 切到任务页 ✓;点「日程」⇒ 切到日程页 ✓
· 悬浮栏几何:栏体 x=21..1060、y=2132..2307(高 58vp),
栏外 x=0..20 为纸色 ⇒ **四周留白** 符合规范 ✓
· 栏内呈半透材质色(#EFEBE2) ⇒ `ImmersiveMaterial` 生效 ✓
· 14 页遍历 **14/14**;深验 **12/12**;编译 **0 error / 0 warning**
## 过程中的一个坑
给 12 个页面补 `FB_DOCK_RESERVE` 的 import 时,脚本统一写成
`'../components/FloatDock'` —— 但**页面在 `pages/<模块>/` 下,需要 `../../`**
(`Index.ets` 在 `pages/` 下才是 `../`)。批量改 import 路径要按**文件深度**区分。
|
2026-10-12 12:06:00 +08:00 |
|
laoyang
|
705a65ae2e
|
v0.5.0 抽屉改「应用内左右滑动」触发 + 去掉汉堡按钮 + 统一区块边距
用户 2026-10-12 三条反馈。
## ① 抽屉改「应用内水平滑动」触发(关键更正)
用户实测指出:「左滑右滑不是从边上滑,而是在应用的内部从左往右滑或者从右往左滑。
如果从边上往里边滑的话,它是系统手势,我们是没有办法去控制它的。」
⇒ **这是之前手势完全没生效的根因**:屏幕边缘是系统手势区(返回/侧边栏),
从边缘往里滑的事件根本轮不到应用。判据改为「在应用内部水平滑动」:
· 向右滑 ⇒ 抽屉从左边出;· 向左滑 ⇒ 抽屉从右边出。
(左手持机往右划、右手持机往左划,单手都顺。)
🔴 同时修掉一个真 bug:**`offsetX` 的单位是 vp**(SDK 原文 "in vp"),
我之前又除以 density,把位移缩到 1/3 ⇒ 阈值永远达不到。
⚠️ 用 `parallelGesture`(不是 gesture):内容区还有竖向滚动与
纪念日/财务/任务的横向滚动条,用 gesture 会把它们抢掉。
阈值 80vp,轻扫横向列表不会误开。
## ② 去掉顶栏汉堡按钮
用户:「那个三个点、三个横线的按钮就不要了,图标靠左,显得更好看一些」。
⇒ TopBar 删掉按钮,`padding.left` 由 14 改为 `pagePad`,图标与下方内容左边线对齐。
## ③ 🔴 统一区块边距(修掉 15 处真 bug)
用户:「页面里的方框大小不一样,有的左右充满,有的有缝隙 —— 最好都留一点距离」。
实测界面树 + 像素采样找到根因:**卡片同时写了 `.width('100%')` 和
`.margin({left, right})` ⇒ width 先把宽度占满、margin 失效,卡片顶到屏幕两侧**。
(首页个人信息卡实测 x=0..1080 满宽,而别的区块是留边 17vp 的。)
全项目扫出 **15 处**同款写法(calendar 3 / days 2 / ledger 2 / movies 1 /
people 2 / settings 5),**全是卡片**(backgroundColor+borderRadius+margin)。
统一删掉那条 `.width('100%')`,宽度交给 margin 撑出。
### 验证(真机)
· 手势:向右滑 ⇒ 抽屉「家书」x=187(贴左);向左滑 ⇒ x=442(贴右)—— 双向 ✓
· 边距:像素级逐行采样,17 行里 15 行左右均 50px(17vp)完全一致;
另 2 行是"标题文字起点"(文字有额外缩进),不是边距问题 ✓
· 14 页遍历 **14/14**;深验(每页特征文本)**12/12**
· 编译 **0 error / 0 warning**
### 工具链修复(都很关键)
· 🔴 `hdc file recv` 在 **Git Bash** 下会被路径转换改写:`/data/local/tmp/x`
被当成 `C:/.../tmp/x` ⇒ **静默失败、界面树永远抓不到**。
三个验证脚本已统一注入 `MSYS_NO_PATHCONV=1`。
· 探针不再点汉堡按钮开抽屉(按钮已删),改用「应用内向右滑」。
· 修探针一处误判:顶栏与抽屉**都有**「家书」,原来没区分 ⇒
两种滑动都读到顶栏那个固定 x,误报"手势没生效"(其实生效了)。
|
2026-10-12 11:37:45 +08:00 |
|
laoyang
|
0816e25988
|
v0.4.0 顶栏改白色 + 通知栏同色 + 顶栏随页面滚动
用户 08:56 的三条要求:
1. 家书标题栏用浅色(下面色块的背景色);
2. 上面通知栏也是浅色,两者融为一体;
3. 向上滑动时标题栏跟着向上滑,不固定在原位。
(追问确认:顶栏底色 = 白;「品牌条 + 页面标题条」一起随滚动消失。)
## ① 系统栏改「不透明 + 与顶栏同色」,弃用透明方案
原来走「系统栏全透明 + 让紧邻元素透出同底色」。这版顶栏要用白色,
若仍走透明方案就得再造一层"白色压条"去刷白状态栏,太绕。
⇒ 直接 `statusBarColor = surface`(白)、`navigationBarColor = page_bg`(纸色)。
这是**配置出来的同色**,比"指望紧邻元素恰好同色"稳得多(后者顶栏一改底色就出色带)。
副产品:不再需要 `setWindowBackgroundColor` ⇒ 连那条 unavailable 告警也消失,
编译回到 **0 error / 0 warning**。
## ② 顶栏随滚动:整页包进 Scroll,顶栏作为内容第一个元素
原来 `Column { 页头; List/Grid(layoutWeight 1) }` —— 顶栏在滚动区**外面**,
所以固定不动。改为 `Scroll { Column { this.topBar(); 页头; 列表 } }`。
顶栏由 Index 以 `@BuilderParam topBar` 插槽下发(`topBarSlot`),13 个页面全部接入。
⚠️ 同步必须处理的两点:
· 列表的 `.layoutWeight(1)` 要删 —— Scroll>Column 里高度由内容决定,
layoutWeight 会去分"剩余空间"(=0) ⇒ 列表塌成 0 高、整页只剩顶栏和页头;
· List/Grid 不能自己再滚动(去 scrollBar/edgeEffect)—— 两层滚动容器会抢手势。
⚠️ ArkUI 限制:带尾随 lambda 的组件只能有 1 个 `@BuilderParam`
(首页原有闲置的 topActions 已删)。
## ③ 顶栏白色 + logo 可辨认
logo 是「米色底(74%) + 砖红图案(24%)」的实心方图,放在米色上底板隐形、
只剩图案发飘(用户说"看不清")。今加一层极浅描边把底板"托"出来。
顶栏高度 56→48vp。
## 验证(真机,全部通过)
· 沉浸式:状态栏色 `#FFFFFF` == 顶栏色 `#FFFFFF` ✓
· 顶栏随滚动:「家书」标题 y=140 → 滑动后**已滚出可视区** ✓
· 顶栏滚走后状态栏仍为 `#FFFFFF`(未露出页面内容)✓
· 14 个页面遍历 **14/14**;深验(每页特征文本)**12/12**
· 编译 **0 error / 0 warning**
## 过程记录:批量改 12 个文件失败两次(已回滚重做)
第 1 版按缩进正则切分 ⇒ 闭合括号错乱;第 2 版跳字符时**没消费 Column 的
修饰符链** ⇒ 吃掉 `@Builder` 声明、**636 个报错**。
第 3 版改为**按行 + 括号深度解析**,且**改完立刻做结构断言**
(括号配平、`this.topBar()` 恰好 +1),不满足就不写入;先单页试跑再推广。
教训:批量改多文件时正则不可靠,必须配结构自检 + 单页试点。
配套修掉 `_tooling/familybook_harmonyos_run.sh` 一个"假成功":
`hdc list targets` 无设备时输出字面量 `[Empty]`,原来"非空即通过"
⇒ 拿 `[Empty]` 当序列号去装、还打印「✓ 设备:[Empty]」。已显式排除并 die。
|
2026-10-12 10:06:55 +08:00 |
|
laoyang
|
d6bda758d8
|
v0.3.0 沉浸式按 SyncCalendar 重做(通知栏与页面完全同色)+ 个人信息卡改白块
## ① 沉浸式:改用本机 SyncCalendar 的方案(用户指定参考)
用户原话:「上面那一块的颜色和通知栏的颜色完全融为一体;
紧接着就是应用图标和标题,尽量靠上」。
三件套(缺一不可):
1. 系统栏涂**全透明** `#00000000`;
2. `setWindowBackgroundColor(页面底色)` 供透明系统栏透出;
3. **紧邻系统栏的元素不设自己的背景色** + 根容器 `expandSafeArea`。
🔴 第三件是最关键、也是我先前漏掉的一环:
我原来给顶栏设了白色 `backgroundColor`,于是状态栏(透出纸色)与顶栏(白)
仍上下拼接。读 SyncCalendar 源码发现它的 `header()` **根本没有 backgroundColor**
—— 这才是"融为一体"的真正机制。已在代码里写明原因,防止回退。
配套简化:删掉自己算安全区的那套(`statusBarH`/`bottomBarH`/
`loadWindowInsets`/`applyAvoidAreas`,共约 110 行)—— 不再手算,也就不会算错。
## ② 顶栏改「图标 + 标题」
按用户要求:应用图标 + 「家书」标题(下行小字显示当前模块)贴左上,
右侧铃铛与头像。顶栏高度 56 → 48vp,上下内边距压到 4,内容整体上移。
## ③ 个人信息卡恢复为白色卡片
用户明确要求("还是得用一个白色的块,这样显得好看")。
曾一度做成"不套卡片的头部"(理由是省首屏高度),现按用户观感改回白卡。
⚠️ 与顶栏的区别:顶栏**不能**有背景色(否则与状态栏割裂),
而这张卡在顶栏下方、与状态栏不相邻,套白卡没有这个问题。
## 验证(全部基于真机)
逐行像素采样(`_tooling/familybook_immersion_check.py`,
模型读不了图 ⇒ 用数字裁定,不靠目测):
· `y=0..271` 全为 `#F7F3EA` ⇒ **状态栏与顶栏同色,融为一体** ✓
· 底部 `y=2224..2444` 为纸色,无异常条 ✓
真机界面遍历(`_tooling/familybook_harmonyos_probe.py`):
· A 组抽屉 11 个模块 **11/11**
· B 组家谱 **1/1**;C 组通知/设置 **2/2**
· D 组深验(特征文本)**12/12**
合计 **14 个页面全通过**。
⚠️ 顺带修掉探针自身两个坑(都记进注释了):
· `nodes_of` 原来只收集**有文字**的节点 ⇒ 找不到纯图标的汉堡按钮,
导致"抽屉打不开"、10 个模块连锁失败(看着像应用坏了,其实是探针瞎了);
· 汉堡按钮坐标写死 ⇒ 顶栏变矮后点空。改为在界面树里动态探测、
并**排除系统状态栏区域**(那里也有可点击元素,曾误取到 (103,56))。
## 编译
0 error(保留 1 条 `setWindowBackgroundColor` 的 unavailable 告警,它是透明方案的必需组成)
|
2026-10-12 08:43:32 +08:00 |
|