v62:纪念日列表修正 / 分类图标与设置菜单 / 首页卡片体系
- 纪念日列表:display:flex 从 td 移到内层 div(写在 td 上会破坏表格列算法, 表现为关联人列与其它列高度不一致);行内列序按表头对齐; 改完再点「新建」不再带出上次修改的数据;文案改为「与我相关」「来自名册」; 别人记的、把我挂成关联人的条目一并显示(split() 返回 hiddenIds, 路由层据此过滤「接下来的日子」,与两张表同一套可见性) - 分类图标:新增 app/AnnivIcon.php(17 个行内 SVG 图标 + 同 key 的 emoji 供日历纯文本处使用,内网无 CDN 不用 FontAwesome);去掉「看日历」按钮; 设置按钮改为菜单,第一项进 days/settings 独立页;分类可自选图标, 列表徽标与日历条目都按分类显示 - 首页卡片:新增 app/HomeCards.php 与 templates/home_cards.php; 8 格宽 × 高 2/4/6/8;首页拆成 templates/home/_card_*.php 共 8 个片段; home/cards 可改尺寸、调顺序、开关
This commit is contained in:
+110
-31
@@ -13,20 +13,39 @@
|
||||
*
|
||||
* ⇒ 三块(顺序按用户第 9 条要求):
|
||||
* ① 接下来的日子(未来 60 天)
|
||||
* ② **手动添加的**纪念日(在上)—— 可改可删;右侧按钮:改=蓝(.btn)、删=黄(.btn-warn)
|
||||
* ③ 来自名册生日的(在下,实时派生、不能改删)
|
||||
* ② **与我相关**的纪念日(在上)—— 可改可删;右侧按钮:改=蓝(.btn)、删=黄(.btn-warn)
|
||||
* ③ 来自名册的生日(在下,实时派生、不能改删)
|
||||
*
|
||||
* @param array $custom 用户自建的(行上带 person_ids)
|
||||
* 🔴 v62(用户 2026-10-18 第 1、2 条)改了四处:
|
||||
* ① 两块标题:「我自己添加的」→「与我相关」、「来自名册的」→「来自名册」,
|
||||
* 并**删掉**那句"这一块是实时派生的…"的说明;
|
||||
* ② 「关于我相关」这一块要收进"别人添加、但把我也挂成关联人"的条目
|
||||
* (判据见 `Anniversary::split()`);
|
||||
* ③ 「看日历」按钮去掉(日历在顶栏就有,页面里再放一个只是多一个入口);
|
||||
* ④ 分类徽标带图标(`AnnivIcon`),日历里用同一图标的 emoji 形态。
|
||||
*
|
||||
* @param array $custom 与我相关的(行上带 person_ids)
|
||||
* @param array $derived 从名册生日派生的(行上也有 person_ids)
|
||||
* @param int $hidden 别人记的、与我无关的条数(只用于一句小字说明)
|
||||
* @param array $upcoming 接下来 60 天的
|
||||
* @param array $kinds 分类清单(设置里维护)
|
||||
* @param array $people 名册(把 person id 换成头像 + 称呼)
|
||||
*/
|
||||
$canWrite = \App\Auth::canWrite();
|
||||
$kindBadge = static function (array $r): string {
|
||||
$famId = (int)\App\Auth::familyId();
|
||||
/* 分类徽标:图标 + 名字(v62 第 2 条「列表中…要根据分类,显示不同的图标」)。
|
||||
⚠️ 图标走 `Anniversary::iconOf()`(单点来源)—— 不要在模板里再写一遍
|
||||
"生日用蛋糕、忌日用蜡烛"的映射,否则设置页改了图标这里不跟着变。 */
|
||||
$kindBadge = static function (array $r) use ($famId): string {
|
||||
$k = (string)($r['kind'] ?? '');
|
||||
return $k === '' ? '' : '<span class="node-badge">' . e($k) . '</span>';
|
||||
if ($k === '') {
|
||||
return '';
|
||||
}
|
||||
return '<span class="node-badge kind-badge">'
|
||||
. \App\AnnivIcon::svg(\App\Anniversary::iconOf($famId, $k), 13, 'kind-ic')
|
||||
. '<span class="kind-name">' . e($k) . '</span></span>';
|
||||
};
|
||||
|
||||
$srcWord = static function (array $r): string {
|
||||
$src = (string)($r['source'] ?? 'custom');
|
||||
if ($src === 'birthday_solar') {
|
||||
@@ -105,10 +124,24 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="ann-people"><?= $peopleCell($r) ?></td>
|
||||
<?php /* 🔴🔴 v62(用户 2026-10-18 第 1 条):「我自己添加的部分,现在显示有错位,
|
||||
好像关联人和其他列,高度不一样。」
|
||||
⚠️ 这里有**两个**独立的错位,别只修一个:
|
||||
① 高度错位(已修):这两格原来把 `display:flex` 直接写在 **`<td>`** 上
|
||||
(`.ann-people` / `.ann-acts`)。`display` 一改成 flex,这个元素
|
||||
**就不再是 table-cell** —— 它从表格的列算法里脱了出来,
|
||||
于是整行的列宽/基线全乱(同一行里有的格按表格排、有的格按 flex 排)。
|
||||
⇒ `<td>` 恢复成普通单元格,flex 放到**里面一层 div** 上。
|
||||
② **内容错位(本次修)**:表头写的是「关联人 | 分类 | 重复」,
|
||||
但行里依次塞的是「分类 | 重复 | 关联人」——
|
||||
三列的内容整体往左挪了一格,用户看到的"关联人"那一栏里其实是分类。
|
||||
⇒ **表头保持原顺序不动**(「关联人」在「分类」前是用户当初指定的),
|
||||
把**行里的顺序**改成跟表头一致:
|
||||
日期 | 纪念日 | **关联人** | **分类** | 重复。 */ ?>
|
||||
<td><div class="ann-people"><?= $peopleCell($r) ?></div></td>
|
||||
<td><?= $kindBadge($r) ?></td>
|
||||
<td class="small muted"><?= e($srcWord($r)) ?></td>
|
||||
<td class="nowrap ann-acts">
|
||||
<td class="nowrap"><div class="ann-acts">
|
||||
<?php if ($editable && $canWrite): ?>
|
||||
<?php /* 用户第 2 条:「右侧按钮,和其他模块的按钮样式保持一致,
|
||||
修改用蓝色,删除用黄色。」
|
||||
@@ -135,7 +168,7 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
<?php else: ?>
|
||||
<span class="small muted">来自名册</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</div></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
@@ -156,8 +189,12 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
data-href="<?= e(url('days/new')) ?>"
|
||||
aria-haspopup="dialog" aria-expanded="false">新建纪念日</button>
|
||||
<?php endif; ?>
|
||||
<a class="btn btn-ghost" href="<?= e(url('calendar')) ?>">看日历</a>
|
||||
<?php /* 用户第 9 条:设置按钮就在「新建纪念日」右边(☰,排最右) */ ?>
|
||||
<?php /* 🔴 v62(用户 2026-10-18 第 2 条):「纪念日,看日历这个按钮去掉。」
|
||||
理由(用户没说的那一半):日历在**顶栏**上一直有,这一页再放一个
|
||||
"看日历"只是同一件事的第二入口,还占掉了本该给「设置」的位置。
|
||||
⚠️ 不要再"改个名字留着" —— 用户说的是去掉。 */ ?>
|
||||
<?php /* 用户第 9 条:设置按钮就在「新建纪念日」右边(☰,排最右)。
|
||||
v62:点开是**菜单**,第一项「分类设置」(可给每个分类配图标)。 */ ?>
|
||||
<?php $setCurrent = 'kinds'; require __DIR__ . '/_settings_menu.php'; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -214,12 +251,34 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
(container || document.body).appendChild(s);
|
||||
s.parentNode.removeChild(s);
|
||||
}
|
||||
function loadEdit(url) {
|
||||
/* 🔴🔴 v62(用户 2026-10-18 第 1 条):「如果我修改的一个纪念日,然后再点击新建纪念日,
|
||||
竟然把我刚才修改的纪念日带进来了,其实不是新建,而是修改。」
|
||||
|
||||
真根因:**「修改」和「新建」共用同一个 `#anNewCard`**(v43 那轮为了"编辑也开弹框"
|
||||
刻意做的)。而「修改」是把 `days/edit?embed=1` 吐回来的那块**替换**掉当前的卡片 ——
|
||||
替换之后,卡片里已经躺着一份带 `input[name=id]` 的编辑表单。
|
||||
这时点「新建」,旧代码只做了一件事:`setOpen(true)`(把同一张卡片显示出来),
|
||||
于是屏幕上就是"刚才那条被编辑的纪念日",按确定也仍然是**更新**而不是新增。
|
||||
|
||||
⇒ 修法:开「新建」之前先看这张卡片**是不是还停在"改过/正在改"的状态**:
|
||||
· 有 `input[name=id]` → 这是编辑表单,必须换回新建表单;
|
||||
· 或者标题框里已经有内容(上次新建填了一半又关掉、或刚保存过一条)→ 同样换新,
|
||||
否则"新建"会带出上一次输了一半的内容,是同一类错觉。
|
||||
⇒ 换新的方式与「修改」完全对称:fetch `days/new`(内嵌片段)替换卡片。
|
||||
⚠️ 只有"脏"的时候才发这一趟请求 —— 干净的弹框直接开,不多一次网络往返。 */
|
||||
function needsFresh() {
|
||||
var c = card();
|
||||
if (!c || !window.fetch) { if (url) { window.location.href = url; } return; }
|
||||
if (!c) { return false; }
|
||||
if (c.querySelector('#anForm input[name="id"]')) { return true; }
|
||||
var t = c.querySelector('#an-title');
|
||||
return !!(t && String(t.value || '').trim() !== '');
|
||||
}
|
||||
function loadForm(url, title) {
|
||||
var c = card();
|
||||
if (!c || !window.fetch || !url) { if (url) { window.location.href = url; } return; }
|
||||
c.hidden = false; // 先打开(显示"载入中"占位)
|
||||
var lb = c.querySelector('#anNewCardTitle, .cal-modal-head b');
|
||||
if (lb) { lb.textContent = '编辑纪念日'; }
|
||||
if (lb) { lb.textContent = title; }
|
||||
fetch(url, { credentials: 'same-origin',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' } })
|
||||
.then(function (r) { return r.text(); })
|
||||
@@ -231,17 +290,29 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
c.parentNode.replaceChild(fresh, c);
|
||||
runInlineScripts(fresh, html);
|
||||
setOpen(true);
|
||||
/* 换了新片段之后,标题框里是空的 ⇒ 后面几次开合都不会再发请求 */
|
||||
var b = document.getElementById('anNewBtn');
|
||||
if (b) { b.setAttribute('aria-expanded', 'true'); }
|
||||
})
|
||||
.catch(function () { window.location.href = url.replace(/[?&]embed=1/, ''); });
|
||||
}
|
||||
document.addEventListener('click', function (e) {
|
||||
var t = e.target;
|
||||
if (!t || !t.closest) { return; }
|
||||
if (t.closest('#anNewBtn')) { e.preventDefault(); setOpen(true); return; }
|
||||
if (t.closest('#anNewBtn')) {
|
||||
e.preventDefault();
|
||||
var btn = t.closest('#anNewBtn');
|
||||
if (needsFresh()) {
|
||||
loadForm(btn.getAttribute('data-href') || '', '新建纪念日');
|
||||
} else {
|
||||
setOpen(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var ed = t.closest('[data-an-edit]');
|
||||
if (ed) {
|
||||
e.preventDefault();
|
||||
loadEdit(ed.getAttribute('data-href') || '');
|
||||
loadForm(ed.getAttribute('data-href') || '', '编辑纪念日');
|
||||
return;
|
||||
}
|
||||
if (t.closest('#anNewX') || t.closest('#anNewCancel')) { setOpen(false); return; }
|
||||
@@ -341,15 +412,17 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php /* ==================== ② 手动添加的(在上,用户第 9 条)==================== */ ?>
|
||||
<?php /* ==================== ② 与我相关的(在上,用户第 9 条)====================
|
||||
🔴 v62(用户 2026-10-18 第 1 条):标题「我自己添加的(4)」+「只有这些能改能删」
|
||||
→ **「与我相关」**(hint 整条删掉)。
|
||||
理由:这一块现在不只有"我添加的",还有"别人添加、但把我挂成了关联人"的
|
||||
(判据见 Anniversary::split())。继续叫"我自己添加的"就是错的;
|
||||
而"只有这些能改能删"这句在**功能上**没必要跟用户解释(按按钮能改能删,一看就懂)。 */ ?>
|
||||
<div class="card card-pad">
|
||||
<h2 class="cinema-h2">
|
||||
我自己添加的(<?= count($custom) ?>)
|
||||
<span class="hint">只有这些能改能删</span>
|
||||
</h2>
|
||||
<h2 class="cinema-h2">与我相关(<?= count($custom) ?>)</h2>
|
||||
<?php if (!$custom): ?>
|
||||
<p class="empty-hint">
|
||||
还没有自己添加的纪念日。
|
||||
还没有与你相关的纪念日。
|
||||
<?php if ($canWrite): ?>
|
||||
结婚纪念日、忌日、相识的日子…点右上角「新建纪念日」加一条。
|
||||
<?php endif; ?>
|
||||
@@ -357,24 +430,30 @@ $renderTable = function (array $rows, bool $editable) use ($canWrite, $kindBadge
|
||||
<?php else: ?>
|
||||
<?php $renderTable($custom, true); ?>
|
||||
<?php endif; ?>
|
||||
<?php /* ⚠️ 别人记的、只跟他自己相关的那些不在这儿出现("与我相关"就该是相关的)。
|
||||
但如果**一条都不提示**,用户可能以为数据丢了 —— 给一句小字,并说清去哪儿找。 */ ?>
|
||||
<?php if ((int)($hidden ?? 0) > 0): ?>
|
||||
<p class="small muted" style="margin:10px 0 0">
|
||||
另有 <?= (int)$hidden ?> 条是别人记的、与你无关,这里不列;日历里照旧能看到。
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php /* ==================== ③ 来自名册的生日(在下)==================== */ ?>
|
||||
<?php /* ==================== ③ 来自名册的生日(在下)====================
|
||||
🔴 v62(用户 2026-10-18 第 1 条):标题「来自名册的(20)」+「实时读出来的,
|
||||
改生日这里跟着变」→ **「来自名册」**;下面那段"这一块是实时派生的…"整段删掉。
|
||||
为什么删:那是**实现说明**,用户看的是日子,不是数据从哪来 ——
|
||||
而"改生日这里跟着变"已经由表格右上那句「来自名册」暗示得够清楚了。 */ ?>
|
||||
<div class="card card-pad">
|
||||
<h2 class="cinema-h2">
|
||||
来自名册的(<?= count($derived) ?>)
|
||||
<span class="hint">实时读出来的,改生日这里跟着变</span>
|
||||
</h2>
|
||||
<h2 class="cinema-h2">来自名册(<?= count($derived) ?>)</h2>
|
||||
<?php if (!$derived): ?>
|
||||
<p class="empty-hint">
|
||||
名册里还没有人填过生日。到「名册 → 编辑成员」把生日填上,这里就会自动出现。
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<?php $renderTable($derived, false); ?>
|
||||
<p class="tip" style="margin-top:10px">
|
||||
这一块是**实时派生**的 —— 改生日,这里跟着变,不用重复维护。
|
||||
不需要在这里新建,也没法单独删(删掉生日就等于删了它)。
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div><?php /* /#anListHost */ ?>
|
||||
</div><?php /* /#anListHost —— 这个 div 只开了一次(见上面 `<div id="anListHost">`),
|
||||
收尾也只能有一次。曾经这一行被复制成两行 ⇒ 整个页面的 div 深度变成 -1,
|
||||
元素跑到容器外(流水线的"div 嵌套"检查抓到的就是这个)。 */ ?>
|
||||
Reference in New Issue
Block a user