v25 第 25 批(9 条):名册分组不刷页 / 纪念日多人 / 首页等高 / 移出不回流 /
名册点击看详情拖动才排序 / 任务复选框与状态 / 财务标签多选 / 微信读书同步与统计 /
导航线上数据与图标
用户本轮 9 条需求(原话见 templates/ 与 app/ 内的逐条注释):
1. 名册:列表里给某人分组改为 AJAX 就地挪卡片(不再整页刷新)。
2. 纪念日:关联人改**多选**(复用 _person_picker,字段 person_ids[]);
自建纪念日的按钮与其它模块统一(改=蓝 .btn、删=黄 .btn-warn);
列表新增「关联人」列并给人名前加头像。
3. 首页:日程/任务两块**完全等高**(align-items:stretch + 固定总高 + flex 子项
min-height:0);修「0 件待办」——数字与右侧列表同源(原来各算各的)。
4. 🔴 名册里被移出的人过一阵又回来:根因是 PersonRoster::seedFromPersons()
**每次 Schema::ensure() 都无条件重灌**(线上实测每跑一次 +1 行)。
修:给它加自守卫(只有在 persons 还留着 group_id/sort_order 的**老库**才跑)。
5. 名册:点击=看详情、**只有拖 ⠿ 手柄**才是排序(原来整行既能点又能拖,语义混乱)。
6. 任务:所有任务前加复选框,任何位置点勾即完成;
状态语义照业界整理为 To Do / Doing / Done 并写出说明(用户要求参考别人的做法)。
7. 财务:类别标签改**多选非互斥**(再点一次取消该类别),上下两图 + 图例 + 表格
全部按同一个集合联动;高亮条刻意不给「合计」(多选下各类金额不能相加)。
8. 微信读书:新增**懒同步**(访问时顺手做,6h TTL,只补空字段不覆盖用户填的)、
封面图**拉下来存本站存储**(原来只存外链 → 看不到)、接官方 /readdata/detail 统计。
9. 导航:线上数据只搬了一半(5 板块 29 站 → 7 板块 67 站 + 67 图标)、
展示页头像不显示 / 搜索按钮错位 / 点登录 404。三个真根因:
· 相对地址在 /nav/ 下全 404 → 新增 Nav::abs();
· 搜索容器丢了 id="search"(CSS 是 #search button.submit 绝对定位);
· Nav::iconKey() 与 SimpleNav 的 icon_key() **算法不一致**(少一次 md5 归一化)
→ 搬来的图标文件名对不上;且 uploads/ 整目录 Require all denied。
新增两个**免登录公开出口** nav_icon.php / nav_avatar.php。
顺带修掉一个「数据损坏级」的真 bug(做第 4 条线上反证时发现的,之前一直没人注意):
app/Region.php 三处用中文标点当 trim/ltrim 的 charlist,而 PHP 的 charlist 是
**按字节**匹配的 —— 「。」= E3 80 **82** → 字节 82 进了可剥集合 →
「层」= E5 B1 **82** 被切掉尾字节 → 非法 UTF-8 → backfillAddress 的 UPDATE 报
`1366 Incorrect string value` → **地址拆分升级从上线起一直在失败被跳过**(每请求报一次)。
改成字符级 preg_replace('/…/u');线上实测 ensure() 现在无报错、待拆行数真的在下降。
验证战绩:
- e2e 1289/0(新增 Region 字节级用例等)
- 真机探针:选人 19/0 · 首页/导航 24/0 · 导航模块 34/0 · b18 暂存+确定 10/0 ·
第23批 52/0
- 桌面几何 428/0 · 移动端几何 680/0 · 交互 92/0 · 窄屏 24/0
- 老库升级 153/0(含 anniversary_people 迁移 + 「再跑 ensure() 名册行数不变」)
- 部署静态断言 180 OK / 0 FAIL · 反向验证 3/3 + Region 三变体各被两条断言同时抓住
- 线上 26 个文件与本地**逐字节一致**;展示页 200、图标 66/66 200、登录 200、
越权/不存在均 404、签发短语后 ensure() 不再报错。
This commit is contained in:
+55
-13
@@ -1,21 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* 纪念日(v24 用户第 13 条;2026-10-03 第 9 条重排)
|
||||
* 纪念日(v24 用户第 13 条;2026-10-03 第 9 条重排;**2026-10-07 第 2 条加「关联人」列**)
|
||||
*
|
||||
* 第 9 条原话:「纪念日中,手动添加的纪念日和来自其他模块的,例如生日的纪念日,
|
||||
* 要分开,手动纪念日在上面。在新建纪念日和右侧,也增加设置按钮,
|
||||
* 可以设置纪念日的分类。」
|
||||
*
|
||||
* 所以这一页三块,顺序按用户要求:
|
||||
* ① 接下来的日子(未来 60 天,两类混在一起 —— 那块本来就是"该过什么日子了")
|
||||
* ② **手动添加的**纪念日(在上)
|
||||
* 第 2 条原话:「纪念日模块,每个纪念日现在只能关联一个人,应该可以关联多个人。
|
||||
* 和选择观影人一样,多选模式。自己添加的纪念日,右侧按钮,和其他模块的按钮
|
||||
* 样式保持一致,修改用蓝色,删除用黄色。再加一列,关联人。列表中,
|
||||
* 人的前面,都加头像。」
|
||||
*
|
||||
* ⇒ 三块(顺序按用户第 9 条要求):
|
||||
* ① 接下来的日子(未来 60 天)
|
||||
* ② **手动添加的**纪念日(在上)—— 可改可删;右侧按钮:改=蓝(.btn)、删=黄(.btn-warn)
|
||||
* ③ 来自名册生日的(在下,实时派生、不能改删)
|
||||
*
|
||||
* @param array $custom 用户自建的
|
||||
* @param array $derived 从名册生日派生的
|
||||
* @param array $custom 用户自建的(行上带 person_ids)
|
||||
* @param array $derived 从名册生日派生的(行上也有 person_ids)
|
||||
* @param array $upcoming 接下来 60 天的
|
||||
* @param array $kinds 分类清单(设置里维护)
|
||||
* @param array $kindCounts 每个分类用了几条
|
||||
* @param array $people 名册(把 person id 换成头像 + 称呼)
|
||||
*/
|
||||
$canWrite = \App\Auth::canWrite();
|
||||
$kindBadge = static function (array $r): string {
|
||||
@@ -33,8 +38,39 @@ $srcWord = static function (array $r): string {
|
||||
return \App\Anniversary::repeatWord((string)($r['repeat_type'] ?? 'solar'));
|
||||
};
|
||||
|
||||
/* 关联人(用户第 2 条:「再加一列,关联人。列表中,人的前面,都加头像。」)
|
||||
—— 用全站统一的 person_chip()(它自带头像 + 我对他/她的称呼 + 本人底色)。
|
||||
⚠️ chips 一律**不传 href**:这一格在一张表格里,`<a>` 套 `<a>` 是非法 HTML,
|
||||
浏览器会把外层结构切开(person_chip 的注释里记过这个坑)。
|
||||
⚠️ $peopleMap 由路由层传进来(一次查全,避免逐行查库);缺失时退化成"有几个显示几个"。 */
|
||||
$peopleMap = $peopleMap ?? [];
|
||||
$peopleCell = static function (array $r) use ($peopleMap): string {
|
||||
$ids = (array)($r['person_ids'] ?? []);
|
||||
if (!$ids) {
|
||||
return '<span class="small muted">—</span>';
|
||||
}
|
||||
$out = '';
|
||||
$n = 0;
|
||||
foreach ($ids as $pid) {
|
||||
$pid = (int)$pid;
|
||||
$p = $peopleMap[$pid] ?? null;
|
||||
if ($p === null) {
|
||||
continue;
|
||||
}
|
||||
// 一个人时正常画;多人时最多画 4 个 + 「+N」,免得一行被撑爆
|
||||
if ($n >= 4) {
|
||||
$rest = count($ids) - 4;
|
||||
$out .= '<span class="node-badge" title="还有 ' . $rest . ' 人">+' . $rest . '</span>';
|
||||
break;
|
||||
}
|
||||
$out .= person_chip($p);
|
||||
$n++;
|
||||
}
|
||||
return $out === '' ? '<span class="small muted">—</span>' : $out;
|
||||
};
|
||||
|
||||
/* 画一张纪念日表。两块(手动 / 派生)共用同一个渲染 —— 复制一遍迟早改漏一处。 */
|
||||
$renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge, $srcWord): void {
|
||||
$renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge, $srcWord, $peopleCell): void {
|
||||
if (!$rows) {
|
||||
return;
|
||||
}
|
||||
@@ -42,7 +78,7 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
<div class="table-wrap">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>日期</th><th>纪念日</th><th>分类</th><th>重复</th><th></th></tr>
|
||||
<tr><th>日期</th><th>纪念日</th><th>关联人</th><th>分类</th><th>重复</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($rows as $r): ?>
|
||||
@@ -52,16 +88,22 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
<?= e((string)$r['title']) ?>
|
||||
<?php if (!empty($r['is_leap'])): ?><span class="small muted">(闰月)</span><?php endif; ?>
|
||||
</td>
|
||||
<td class="ann-people"><?= $peopleCell($r) ?></td>
|
||||
<td><?= $kindBadge($r) ?></td>
|
||||
<td class="small muted"><?= e($srcWord($r)) ?></td>
|
||||
<td class="nowrap">
|
||||
<td class="nowrap ann-acts">
|
||||
<?php if ($editable && $canWrite): ?>
|
||||
<a class="link-quiet" href="<?= e(url('days/edit', ['id' => (int)$r['id']])) ?>">改</a>
|
||||
<?php /* 用户第 2 条:「右侧按钮,和其他模块的按钮样式保持一致,
|
||||
修改用蓝色,删除用黄色。」
|
||||
→ 改 = `.btn`(主按钮的**蓝**),删 = `.btn-warn`(**黄**实心)。
|
||||
⚠️ 原来用的是 `.link-quiet`(纯文字链接)+ `.btn-ghost`(白底描边),
|
||||
两者都"看不出来是什么动作",与观影/旅程行上那套不一致。 */ ?>
|
||||
<a class="btn btn-sm" href="<?= e(url('days/edit', ['id' => (int)$r['id']])) ?>">修改</a>
|
||||
<form method="post" action="<?= e(url('days/destroy')) ?>" class="res-inline">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="id" value="<?= (int)$r['id'] ?>">
|
||||
<button class="btn btn-ghost btn-sm" type="submit"
|
||||
onclick="return confirm('删掉这条纪念日?')">删</button>
|
||||
<button class="btn btn-warn btn-sm" type="submit"
|
||||
onclick="return confirm('删掉这条纪念日?')">删除</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<span class="small muted">来自名册</span>
|
||||
|
||||
Reference in New Issue
Block a user