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;
六条已在真机逐条取像素/布局树确认。
This commit is contained in:
@@ -39,7 +39,8 @@
|
||||
|
||||
import { FbColors, FbMetric } from '../../common/Theme';
|
||||
import { FbAppState, fbAppState } from '../../common/Store';
|
||||
import { FbCalEvent, FbHomeData, FbMark, FbMemoryItem, FbTaskItem, FbWeather } from '../../model/Types';
|
||||
import { FbCalEvent, FbHomeData, FbMark, FbMemoryItem, FbRecentDay, FbTaskItem,
|
||||
FbWeather } from '../../model/Types';
|
||||
import { parseHome } from '../../data/FbParse';
|
||||
import { loadModule } from '../../data/FbModuleLoad';
|
||||
import { FB_CAL_SETTINGS, FbCalStyle } from '../../data/FbCalSettings';
|
||||
@@ -91,6 +92,8 @@ export struct FbHomePage {
|
||||
@Local statOverdue: number = 0;
|
||||
/** 往年的今天(`/home` 的 memories) */
|
||||
@Local memories: FbMemoryItem[] = [];
|
||||
/** 🔴 v59:最近三个纪念日(`/home` 的 recent_days,服务端已排好序) */
|
||||
@Local recentDays: FbRecentDay[] = [];
|
||||
/** 顶部提示(同步失败等) */
|
||||
@Local syncNote: string = '';
|
||||
/**
|
||||
@@ -129,6 +132,7 @@ export struct FbHomePage {
|
||||
this.statTodo = d.stat.todo;
|
||||
this.statOverdue = d.stat.overdue;
|
||||
this.memories = d.memories;
|
||||
this.recentDays = d.recentDays;
|
||||
},
|
||||
(note: string): void => {
|
||||
this.syncNote = note;
|
||||
@@ -425,10 +429,58 @@ export struct FbHomePage {
|
||||
.width('100%')
|
||||
.padding({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12 })
|
||||
|
||||
// ==================== ④ 往年的今天 ====================
|
||||
// ==================== ④ 最近纪念日 ====================
|
||||
// 🔴🔴 v59(用户 2026-10-18):
|
||||
// 「首页应该加一个『最近纪念日』模块,放在『往年的今天』前面、
|
||||
// 『近期任务』后面。内容包括:
|
||||
// 1. 未来最近的三个纪念日,包括每个人的生日,以及手动填写的各个纪念日。
|
||||
// 2. 以倒计时形式展示:距离哪个纪念日还有多少天,
|
||||
// 距离谁谁谁的生日还有多少天。」
|
||||
// ⇒ 位置就卡在这两段之间(**不是**"放在首页某处")。
|
||||
// ⇒ 数据来自 `/home` 的 `recent_days`(服务端已排好序、最多 3 条),
|
||||
// 来源是"派生生日 + 自建纪念日",正好覆盖用户说的两样。
|
||||
Column({ space: 0 }) {
|
||||
FbSectionTitle({
|
||||
title: '最近纪念日',
|
||||
extra: this.recentDays.length > 0
|
||||
? this.recentDays.length.toString() + ' 个'
|
||||
: ''
|
||||
})
|
||||
Column({ space: 0 }) {
|
||||
if (this.recentDays.length === 0) {
|
||||
Column({ space: 6 }) {
|
||||
Text('还没有要过的日子')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(FbColors.ink)
|
||||
Text('在「纪念日」里添一个,或者给家人的资料里填上生日。')
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(FbColors.muted)
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.padding({ top: 12, bottom: 12 })
|
||||
} else {
|
||||
ForEach(this.recentDays, (d: FbRecentDay, idx: number) => {
|
||||
this.recentDayRow(d, idx === this.recentDays.length - 1)
|
||||
}, (d: FbRecentDay, idx: number): string =>
|
||||
'rd_' + d.date + '_' + d.title + '_' + idx.toString())
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ left: FbMetric.cardPad, right: FbMetric.cardPad, top: 4, bottom: 6 })
|
||||
.backgroundColor(FbColors.surface)
|
||||
.borderRadius(FbMetric.radius)
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12 })
|
||||
|
||||
// ==================== ⑤ 往年的今天 ====================
|
||||
// 🔴 用户 2026-10-17:「近期任务的下面,应该加上**往年的今天**,
|
||||
// 跟电脑端一样,你看看那个是什么逻辑。」
|
||||
// ⇒ 位置**就在近期任务下面**(用户明确指定,不是"放在首页某处")。
|
||||
// ⚠️ v59 起它上面多了一块「最近纪念日」(用户 2026-10-18 指定插在
|
||||
// 「近期任务之后、往年的今天之前」)—— 两块都是"日子"类信息,
|
||||
// 顺序是用户定的,别再调。
|
||||
// 电脑端的逻辑见 `App\Home::memories()`:只认**往年的同一天**
|
||||
// (不退回同月),来源是「看过的电影」+「走过的**实际行程**」,
|
||||
// 合并按时间倒序取 6 条。
|
||||
@@ -643,4 +695,106 @@ export struct FbHomePage {
|
||||
private memoryDate(s: string): string {
|
||||
return s.length >= 10 ? s.substring(0, 10) : s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 「最近纪念日」里的一行(v59,用户 2026-10-18)。
|
||||
*
|
||||
* 形态:**倒计时徽标**(`还有 12 天`)+ 标题 + 日期/周岁。
|
||||
* 用户要的就是这个倒计时:「距离哪个纪念日还有多少天,
|
||||
* 距离谁谁谁的生日还有多少天」—— `title` 里已经带了姓名
|
||||
* (服务端拼的「老杨 的生日」),所以一句话读完就是用户要的表达。
|
||||
*
|
||||
* ⚠️ 与 `memoryRow` 同一个反模式提醒:这里也是**两参数 @Builder**。
|
||||
* 它安全的前提是"整块由 ForEach 按 key 重建"(数据一次性到齐)——
|
||||
* 若将来出现"数据变了但行文字没变",**先改成组件**(如 `FbStatCell`),
|
||||
* 别在这个 Builder 上打补丁。
|
||||
*/
|
||||
@Builder
|
||||
recentDayRow(d: FbRecentDay, isLast: boolean) {
|
||||
Column({ space: 0 }) {
|
||||
Row({ space: 10 }) {
|
||||
// ---- 倒计时徽标:把"还剩几天"做成一眼可见的那一块 ----
|
||||
// 今天那种用实底强调(它是"就是今天",比"还有 N 天"更该被看见)
|
||||
Text(this.countdownText(d.days))
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontWeight(d.days === 0 ? FontWeight.Medium : FontWeight.Normal)
|
||||
.fontColor(d.days === 0 ? Color.White : this.recentTint(d.source))
|
||||
.backgroundColor(d.days === 0 ? this.recentTint(d.source) : this.recentWash(d.source))
|
||||
.borderRadius(FbMetric.radiusPill)
|
||||
.padding({ left: 9, right: 9, top: 3, bottom: 3 })
|
||||
.textAlign(TextAlign.Center)
|
||||
|
||||
// ---- 标题(已含姓名 / 纪念日名)----
|
||||
Text(d.title)
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(FbColors.ink)
|
||||
.layoutWeight(1)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
|
||||
// ---- 右侧:日期(+ 周岁 / 第几个,有才显示)----
|
||||
Text(this.recentMeta(d))
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(FbColors.faint)
|
||||
.maxLines(1)
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ top: 11, bottom: 11 })
|
||||
.alignItems(VerticalAlign.Center)
|
||||
|
||||
if (!isLast) {
|
||||
Divider()
|
||||
.color(FbColors.line2)
|
||||
.margin({ left: 18 })
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒计时文案。
|
||||
*
|
||||
* ⚠️ `days === 0` 必须说「就是今天」而不是「还有 0 天」——
|
||||
* 后者读起来像"已经过了",正是最容易搞反的地方。
|
||||
*/
|
||||
private countdownText(days: number): string {
|
||||
if (days <= 0) {
|
||||
return '就是今天';
|
||||
}
|
||||
return '还有 ' + days.toString() + ' 天';
|
||||
}
|
||||
|
||||
/**
|
||||
* 右侧那行小字:日期 +(周岁 / 第几个)。
|
||||
*
|
||||
* · 日期一律显示(`MM-DD`,年份对"年年过"的纪念日没有信息量);
|
||||
* · `age >= 0` ⇒ 生日,附「N 岁」(0 岁是**真实存在**的,不能当"没有");
|
||||
* · `nth >= 1` ⇒ 用户填了起始年,附「第 N 个」。
|
||||
*/
|
||||
private recentMeta(d: FbRecentDay): string {
|
||||
const md: string = d.date.length >= 10 ? d.date.substring(5, 10) : d.date;
|
||||
const parts: string[] = [md];
|
||||
if (d.age >= 0) {
|
||||
parts.push(d.age.toString() + ' 岁');
|
||||
}
|
||||
if (d.nth >= 1) {
|
||||
parts.push('第 ' + d.nth.toString() + ' 个');
|
||||
}
|
||||
return parts.join(' · ');
|
||||
}
|
||||
|
||||
/** 纪念日的强调色:生日用粉、其余用紫(与网页版 `hb-mark` 的 birthday/anniv 一致) */
|
||||
private recentTint(source: string): string {
|
||||
return this.isBirthday(source) ? '#A03060' : '#6B4A86';
|
||||
}
|
||||
|
||||
/** 对应的极浅底(与上面同一档色的淡化) */
|
||||
private recentWash(source: string): string {
|
||||
return this.isBirthday(source) ? '#FDECF3' : '#F2ECF7';
|
||||
}
|
||||
|
||||
/** 是不是"生日"那类(服务端派生的生日条目;`custom` 是用户自己填的纪念日) */
|
||||
private isBirthday(source: string): boolean {
|
||||
return source === 'birthday_solar' || source === 'birthday_lunar';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user