v53:纪念日记年份算「第几个」/ 首页待办排在进行中下面 / 日程编辑浮层跨满整行 / 新增成员打标签 / 新增页一次填完称呼与备注
用户 2026-10-16 五条。
① 纪念日要记录年份,才能知道「第几个」
用户原话:「纪念日需要记录年份,这样才能知道**是第几个纪念日**。」
**真根因有两层**(两处一起才构成"没有年份"):
· 表里没有:anniversaries 只有 month + day(当年当成"一年一度,年份无意义");
· 表单有、但被丢掉:日期控件本来就带年份(<input type="date"> 是 YYYY-MM-DD),
而拆分 JS **只取 mm/dd**(`mo.value = m[2]; dy.value = m[3];`)⇒ 想存也没存上。
修法:anniversaries.year(SMALLINT UNSIGNED NULL)+ 升级路径 addColumn;
表单补 hidden year、拆分 JS 一并写;Anniversary::save 收 year(**区间守卫 1900..2100**);
occurrences() 输出 year 与 nth;列表页显示「2018 年起 · 第 9 个」。
⚠️ 年份**可空、且不给默认值**:老数据就是没有年份,猜一个(按创建年之类)
会让页面算出一个**错的**"第 N 个"。NULL = 只按月日过、不显示序号 —— 诚实。
⚠️ 顺带修一个自相矛盾:repeat_type='once' 原来落在公历分支 ⇒ **年年都冒出来**;
现在有年份了 ⇒ 只在那一年出现(没年份的保持老行为,不凭空丢)。
🔴 **序号算法抽成单点来源 `Anniversary::nthFor()`** —— `occurrences()`(临近区)与
days/index.php(我添加的那张表)**都调它**;两处各写一遍是"改一处忘一处"的温床。
用**今天所在的年**("今年是第几个"),不是"下一次发生那年"。
🔴🔴 **踩坑(最值钱)**:`addColumn`(ALTER TABLE)**必须排在那张表 CREATE TABLE 之后**。
第一版放进 movie_details 那段(anniversaries 的建表更靠后)⇒ 全新安装
`1146 Table 'fb_test.anniversaries' doesn't exist` ⇒ 整段 ensure() 抛异常 ⇒
**install 直接"安装失败"**(POST 回 200、不自删、schema_version=None、库一片空白)。
新建库走 CREATE TABLE、老库走 addColumn —— **两条路都要在**。
② 首页近期任务:把待办也放进来(排在进行中下面)
用户原话:「首页的近期任务,**把待办也放进来吧,放在进行中的下面**。」
原来只收「进行中 + **已逾期**的待办」(当年怕待办一多首页变长清单)。
⇒ 现在**全部**收进来、按**状态分组**(doing 在前、todo 在后);
两组内部仍按截止时间升序(byStatus 的 ORDER BY 已排好)。
⚠️ 配套改统计:`count($tasks) 件在做` 会把待办也算成"在做" ⇒ 改成**按状态拆开数**
(进行中→「件在做」、待办→「件待办」,两数相加=列表行数,仍同源)。
「件待办」原读 stat7['todos'](**与列表不同源**,v24 踩过"数字和列表对不上")。
⚠️ 空态:「没有进行中的任务,也没有待办。」
⚠️ .hd-pane-body 本来就是 overflow-y:auto + flex:1 1 auto/min-height:0,行数变多**内部滚动**、
卡片高度不变(v32"两块外框恒等高"那套兜住了)。
③ 日程编辑浮层「只有左侧有内容,右侧空白,好像分栏了」
**真根因**(assets/css/app.css):日程编辑页 event.php 里,`.mv-grid`(观影式**纵向**列表)
被嵌在**两列**的 `.form-grid` 里 ⇒ 它只占**第一列**。整页形态下看不出来(页面宽),
但**浮层里面板只有 620px** ⇒ 两列各 286px ⇒ 所有字段挤在左边 286px、右边整列空着。
实测:panel=620 / form-grid=588 / cols="286px 286px" / mv-grid w=286
→修后 mv-grid w=588(95% of panel)、标题输入框 w=516、距右缘 ~2px。
修法:`.form-grid > .mv-grid { grid-column: 1 / -1; }` —— **一行 CSS**。
⚠️ 不去改 event.php 里"整页保留两列、浮层用 mv-grid"那个结构判断(那是有意保留的);
`.mv-grid` 本身就是"一整块、占满宽度"的语义,天生不该被塞进半列。
⚠️ 排查手法:这种"看起来像分栏"的问题**量几何比读 CSS 快** ——
getBoundingClientRect 打出 panel/grid/cols/内层宽,一眼看出"嵌了几层 grid"。
④ 添加成员时是**打标签**,不是选分组
用户原话:「名册中添加成员时,**不是选择分组,而是打标签**。现在已经没有分组的概念了,
**只有标签的概念**。」
⇒ 新增分支的「所属分组」下拉(group_id)换成**标签输入**(复用 v51 的 #ptBlock 那套:
候选勾选 + 自由输入,**文本框是唯一提交源**)。
⚠️ 只在**新增**时出现在那个位置(编辑时上面那个 #ptBlock 已在管标签,出现两块会让人以为填两遍)。
⚠️ **分组表结构 person_roster.group_id 保留不动**(名册"拖动归档"还在用、历史数据也在),
只是**新增入口不再要求人选**。
⚠️ e2e 里 v14「新增表单**有**分组下拉」与新需求正相反 ⇒ 改成「没有 group_id + 有 name=tags」。
⑤ 添加成员页一次填完「我对他的称呼 + 我的备注」
用户原话:「我在添加一个成员的时候,是不是可以直接在添加成员的页面里填写**我对他的称呼**
以及**我对他的备注**,**一次就解决**?要不然还得先添加、再返回他的名册去填,有点麻烦。」
⇒ 新增页也出现这两栏:
· **我对他的备注**(person_notes)—— 条件从 `$isEdit && !$isSelf` 改成 `!$isSelf`;
· **我对他的称呼**(nicknames)—— **新加的输入框** `name="my_nick"`。
服务端 people/create 落两表(挂**我**名下:viewer=我, target=他)。
⚠️ 判"表单里有没有这一栏"用 **array_key_exists**(不是判值是否为空)——
与编辑路径同一口径:**空串是明确的"清空"语义**(Nickname::set('') 会删行)。
⚠️ 四处**重渲染路径**(撞名确认/校验失败/手机号重复/people/new 初始)都要带上
myTags/tagChoices/myNick,否则一撞名或一报错,刚打的标签与刚写的称呼备注就全没了。
⚠️ 编辑页也补 myNick 回显(否则"新增时填了、编辑页看不见"会以为没存上)。
验证(新增 2 套件 + 更新 e2e/deploy 断言):
· _fb_v53_ui.py(**26 条**真机断言)
· _b53_falsify.py(**10 注入 / 10 全抓**,基线 26/0)
· e2e **1517/0**;familybook_deploy.sh 加 v53 静态断言段(14 正向 + 1 反向)
· 全流水线 **PUSH_RC=0**、部署 0 FAIL(含"第53批形态")
· 线上 8 文件 md5 逐字节一致;`anniversaries.year` 已由升级路径补上(nullable smallint);
线上实测 nthFor 4 用例全对(2018→第9个 / null→NULL / 2019→2 / 未来年→NULL);
诊断页已删并复验 404
⚠️ 本轮踩坑(已写进 pitfalls.md / 工作日志):
· **`addColumn` 必须在同表的 CREATE TABLE 之后**(否则全新安装 `1146` ⇒ ensure() 抛异常);
· **探针里 `dispatchEvent(new Event('submit'))` 不会真的提交表单**
(不导航、不发请求)⇒ 判"存没存上"必须真点提交按钮或 `requestSubmit()`;
· **反向验证的 `restore()` 不能只是"再跑一遍 build_work()"**(指纹闸门会把它变空操作
⇒ 第二轮基线是脏的,26/0 掉到 14/12);恢复必须**无条件** copy2;
· **注入锚点在源码里出现两次 ⇒ 打到别处 ⇒ 那条断言恒不红**
(v53 给新增页也加了 `id="ptBlock"`,打偏了 v51 那条"编辑页标签块"的注入);
· **别把两个共用 fb_test 库的套件并行跑**(互相删播种行 ⇒ 看着像"整套功能坏了");
· **部署静态断言的锚点引号/反斜杠必须与源码一字不差**:`\[` 在 bash 双引号里是
**字面反斜杠**、单双引号不能混、漏个 `(int)` 也搜不到 —— 一轮里连红 4 条全是这个。
This commit is contained in:
+58
-21
@@ -194,25 +194,36 @@ $ctHints = [
|
||||
|
||||
<?php /* ⚠️ 这里**没有**「排序号」(已彻底取消,用户原话:「排序号,不要让用户填写」),
|
||||
名册里的先后靠**拖动卡片**调。
|
||||
⚠️「所属分组」**只在新增时出现**($isEdit 为假):
|
||||
分组是"我和他的关系",写在我自己的名册条目上 ——
|
||||
· 新增时这个人还不存在,别人名册里不可能有他,这里选了只影响我自己,
|
||||
正是用户要的「增加名册时可选择所属分组」;
|
||||
· 编辑时**不能**在这里给:我把他归成"同事"不该把妈妈那边的归类也改掉 ——
|
||||
改分组走名册里拖动,或那张卡片的「调整分组」。 */ ?>
|
||||
|
||||
🔴🔴 v53(用户 2026-10-16 第 4 条):「名册中添加成员时,**不是选择分组,
|
||||
而是打标签**。现在已经没有分组的概念了,**只有标签的概念**。」
|
||||
⇒ 原来这里是一个「所属分组」下拉(group_id)。现在改成**标签输入**,
|
||||
与「编辑成员」页那个 #ptBlock 同一套(候选勾选 + 自由输入,文本框是唯一提交源)。
|
||||
⚠️ 只在**新增**时出现在这个位置(编辑时上面那个 #ptBlock 已经在管标签了,
|
||||
同一张表单里出现两块"标签"会让人以为要填两遍)。
|
||||
⚠️ 分组(person_roster.group_id)**表结构保留不动** —— 名册里还靠它
|
||||
做"拖动归档"的容器,历史数据也在。只是**新增入口不再要求人选它**。 */ ?>
|
||||
<?php if (!$isEdit): ?>
|
||||
<div class="field">
|
||||
<label for="group_id">所属分组</label>
|
||||
<select class="select" id="group_id" name="group_id">
|
||||
<option value="0">先不分类</option>
|
||||
<?php foreach (\App\PersonGroup::orderFor((int)\App\Auth::familyId(), (int)\App\Auth::personId()) as $g): ?>
|
||||
<option value="<?= (int)$g['id'] ?>"
|
||||
<?= (int)old('group_id', 0) === (int)$g['id'] ? 'selected' : '' ?>>
|
||||
<?= e((string)$g['name']) ?><?= (int)$g['owner_id'] === 0 ? '(公共)' : '' ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<span class="tip">加进来之后也能改:在名册里把他拖到那一栏就行。</span>
|
||||
<div class="field full" id="ptBlock">
|
||||
<label for="pfTags">标签</label>
|
||||
<?php if (!empty($tagChoices)): ?>
|
||||
<div class="pt-picks pf-picks">
|
||||
<?php foreach ($tagChoices as $tg): ?>
|
||||
<label class="pt-pick">
|
||||
<input type="checkbox" value="<?= e((string)$tg) ?>"
|
||||
<?= in_array((string)$tg, array_map('strval', (array)($myTags ?? [])), true) ? 'checked' : '' ?>>
|
||||
<span>#<?= e((string)$tg) ?></span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input class="input" id="pfTags" name="tags" maxlength="300"
|
||||
value="<?= e(implode(' ', (array)($myTags ?? []))) ?>"
|
||||
placeholder="如:亲戚 球友 同事(用空格分开)" autocomplete="off">
|
||||
<span class="tip">
|
||||
一个人可以有多个标签 —— 既是亲戚又是球友,两个都打上就行。<br>
|
||||
<b>标签是你自己名册里的</b>:你给他打「球友」不会让全家的名册都多出这个词。
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -394,10 +405,17 @@ $ctHints = [
|
||||
<?php /* 用户第 5 条:**我对他的私人备注** —— 只有我自己看得到(存在 person_notes 里)。
|
||||
只在"编辑别人"时出现(自己看自己不需要"我对我的备注")。
|
||||
⚠️ 它是独立的出口(自己的路由 + 自己的表),不与上面那份混在一起:
|
||||
「他愿意公开说的」与「我对他的私下记录」是两件事、两种可见性。 */ ?>
|
||||
<?php if ($isEdit && !$isSelf): ?>
|
||||
「他愿意公开说的」与「我对他的私下记录」是两件事、两种可见性。
|
||||
|
||||
🔴🔴 v53(用户 2026-10-16 第 5 条):「我在添加一个成员的时候,是不是可以
|
||||
直接在添加成员的页面里填写**我对他的称呼**以及**我对他的备注**,
|
||||
**一次就解决**?要不然的话,我还得先添加,然后再返回这个人的名册,
|
||||
再去填写我对他的备注和称呼。这样其实有点麻烦。」
|
||||
⇒ 「新增」时也要出现(原来只在"编辑别人"时才有)——
|
||||
与下面的「我对他的称呼」一起,新增页一次填完。 */ ?>
|
||||
<?php if (!$isSelf): ?>
|
||||
<div class="field full">
|
||||
<label for="myNote">我对 <?= e((string)$person['name']) ?> 的备注(只有我自己看得到)</label>
|
||||
<label for="myNote">我对 <?= $isEdit ? e((string)$person['name']) : '他' ?> 的备注(只有我自己看得到)</label>
|
||||
<textarea class="input" id="myNote" name="my_note" rows="2"
|
||||
placeholder="怎么认识的、要注意什么、上次见面聊了什么……"><?= e((string)($myNote ?? '')) ?></textarea>
|
||||
<span class="tip">
|
||||
@@ -407,6 +425,25 @@ $ctHints = [
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* 🔴 v53(用户 2026-10-16 第 5 条):**我对他的称呼**。
|
||||
称呼是**视角属性**(妈妈叫"明明"、姐姐叫"小弟"),存在 nicknames 表里、
|
||||
按 (看的人, 被看的人) 一对一条 —— 所以它天然是"我的"称呼,不会改到别人。
|
||||
⚠️ 与上面「我对他的备注」并排放在一起:用户把这两件事当成"添加时一次填完"
|
||||
的一组(他原话把「称呼」和「备注」并列)。
|
||||
⚠️ 新增时也给(此时 $person 还不存在,标题用"他")。 */ ?>
|
||||
<?php if (!$isSelf): ?>
|
||||
<div class="field full">
|
||||
<label for="pfNick">我对 <?= $isEdit ? e((string)$person['name']) : '他' ?> 的称呼</label>
|
||||
<input class="input" id="pfNick" name="my_nick" maxlength="40"
|
||||
value="<?= e(old('my_nick', (string)($myNick ?? ''))) ?>"
|
||||
placeholder="如:大哥、明明、王老师(只有我自己看到)" autocomplete="off">
|
||||
<span class="tip">
|
||||
你在他名册里看到的名字会变成这个称呼 —— 妈妈叫「明明」、姐姐叫「小弟」互不影响。<br>
|
||||
留空就用他自己的名字。
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* 🔴🔴 v51(用户 2026-10-15 第 4 条):「名册这个功能里面,当我编辑资料的时候,
|
||||
在这个页面竟然**没有办法选择它的标签**。按道理讲,名册编辑成员这个地方
|
||||
应该能编辑这一个人的**所有信息**才对。」
|
||||
|
||||
Reference in New Issue
Block a user