v71c:首页「通知消息」改成正规卡片,样式直接复用通知页

用户 2026-10-21:
「先把web端的首页修一下,通知消息这个,也是首页的卡片,可以调整顺序和大小。
 另外,这个样式也不好看,我看个人头像点击后,通知消息那个页面的样式就不错,
 直接复用就可以了。」

① 卡片化(HomeCards 注册表 + 默认布局 + 取数)
   · TYPES 新增 'notif'(w4/h4,route=notifications,more=全部通知)
   · DEFAULT_LAYOUT 钉在 brief 之后,与 days 并一行(宽 4)——
     若插在 brief 与 agenda 之间,agenda/tasks 会被挤成一上一下(实测 C13 顶边 407 vs 751)
   · data() 加 case 'notif' → 新增 notifData():最新 10 条 + 未读数,
     与原来的取数口径一字不差;关掉卡片就不查这两条 SQL
   · templates/home.php 删掉网格前那块写死区块(v30 第 5 条「必须在往年的今天前面」
     这条约束作废 —— 那时还没有卡片体系);index.php 不再单独传 notifs/notifUnread

② 样式复用 templates/notifications/index.php
   · 结构与通知页完全一致:头像在前 → 正文 → kind 图标在右(带 ntf-kind-{kind})
     (旧首页那块是"图标在前、头像第二",正是用户说不好看的那一版)
   · 新增 .hc-ntf 作用域只补"卡片比整页小"的三条差异:正文允许折两行(宽 2 时
     单行省略会把正文全吃掉)、时间挪到正文下一行、未读数做小注脚
   · 不带每条右侧「删除」按钮(4×4 的卡里塞两个按钮会把正文挤成两三个字),
     删除在通知页做,卡片右上角有「全部通知」

③ 顺带修掉 v63 A1 抓住的一处:'notif' 的 mod 给 'home' 而不是空串
   (通知不在 Module::LIST,但它是首页自己的卡片;空串会让「全部通知」成为
   一排同色模块按钮里唯一白底 ghost 的那个)
   ⇒ TYPES docstring 立规矩:route 非空就必须有 mod,认不出归哪个模块就归 home

验证(familybook_push.sh 第 6 轮全绿,PUSH_RC=0,2h11m)
   v62 首页卡片 56/0 · v62 反向验证 14/0(新增 M8–M12 五条注入)
   b30 通知 16/0 · b30 反向验证 18/0
   v63 40/0 · v63 反向验证 51/0 · e2e 1523/0 · 部署静态断言空跑 RC=0

同批带上上一轮 v70/v71 的日历修复(已单独验过,本轮流水线一并回归):
   · v70 日历导入后条目不显示:+N 条从纯文本 span 改成真按钮,点开是当天全部
     条目浮层;AJAX 新建后就地插入与服务端同形(回 entry,不让前端自己拼)
   · v71 ICS 导入顺序:主事件 → 覆盖行 → EXDATE 由 Calendar::importIcs() 统一保证
This commit is contained in:
2026-10-21 13:22:37 +08:00
parent b2b103df0f
commit b45cf671d1
9 changed files with 810 additions and 83 deletions
+6 -45
View File
@@ -19,9 +19,12 @@
*
* ⚠️ 别在模板里自己算"该多宽":宽高由 `$homeCards`(用户设置)给,
* 模板只负责把类名拼对(`hc-w4 hc-h8`)。
* ⚠️ 通知块仍在**卡片网格之前**:v30 第 5 条要求它出现在「往年的今天」前面,
* 而"往年的今天"现在是网格里的一张卡片,位置由用户定 —— 放在网格前是唯一
* 无论用户怎么摆都成立的方案。
*
* 🔴 v71c(用户 2026-10-21)推翻了一条旧约束:「通知消息这个,也是首页的卡片,
* 可以调整顺序和大小。」—— 它原来在本文件里是一块**网格之前**的写死区块
* (v30 第 5 条当时还没有卡片体系,只能用"排在网格前"保证它在「往年的今天」上面)。
* 现在它是一张**正规卡片**(`_card_notif.php`):位置/宽高/开不开都由用户定。
* ⇒ 这条"必须在网格前"的约束**到此为止**,别再按它往网格上加写死区块。
*
* @var array $homeCards HomeCards::layout() 的结果(每项 k/w/h/n)
* @var array $cardData HomeCards::data() 取的那几张卡片的额外数据
@@ -78,48 +81,6 @@
</div>
</section>
<?php /* ==================== 通知消息(v30 第 5 条)====================
用户原话:「…发一个通知,通知出现在首页中,**往年今天的前面**。」
⇒ 放在卡片网格**之前**:往年的今天是网格里的一张卡片,位置由用户摆,
只有"在网格前面"才能保证永远在它上面。
⚠️ 没有通知时整块不渲染 —— 首页是"每天第一眼看的地方",不要用一个空盒子占一屏。 */ ?>
<?php if (!empty($notifs)): ?>
<section class="section">
<div class="page-head" style="margin-bottom:12px">
<h1 style="font-size:15px">
通知消息
<?php if ($notifUnread > 0): ?><span class="sub"><?= (int)$notifUnread ?> 条未读</span><?php endif; ?>
</h1>
<div class="actions">
<a class="btn btn-ghost btn-sm" href="<?= e(url('notifications')) ?>">全部通知</a>
</div>
</div>
<div class="card card-pad">
<ul class="ntf-list ntf-list-home">
<?php foreach ($notifs as $n): ?>
<?php
$actorName = \App\Nickname::display(
(int)$meId,
['name' => $n['actor_name'] ?? '', 'my_nickname' => $n['actor_nick'] ?? '']
);
if (trim((string)$actorName) === '') { $actorName = '有人'; }
?>
<li class="ntf-row ntf-row-<?= e((string)($n['kind'] ?? '')) ?><?= empty($n['is_read']) ? ' is-unread' : '' ?>">
<span class="ntf-kind" aria-hidden="true"><?= \App\Notification::icon((string)($n['kind'] ?? '')) ?></span>
<img class="ntf-av" src="<?= e(avatar_url($n['actor_avatar'] ?? null)) ?>" alt="">
<a class="ntf-main" href="<?= e(\App\Notification::href($n)) ?>">
<span class="ntf-title">
<b><?= e((string)$actorName) ?></b><?= e(\App\Notification::bodyWithoutActor((string)$n['title'])) ?>
</span>
<span class="ntf-time"><?= e(fb_time_ago((string)$n['created_at'])) ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
</section>
<?php endif; ?>
<?php /* ==================== 卡片网格(v62)====================
⚠️ `require` 进来的每个 `_card_*.php` 与这里**同一作用域**(共享 $tasks / $todayEvents
/ $cardData …):这是刻意的 —— 卡片的取数与首页的取数必须同源,