Files
laoyang 37ba86321b v29 第 29 批(9 条):日程定位/复选框提示/名册错乱三处真因修复 + 读书落盘 + 导航四设置 + 笔记阅读态与共享
前 4 条是"上批改了但用户仍报"的,这次先拿真机证据再动手,三处真因如下:

1. 首页没定位到红线 —— 真根因:定位脚本的 PHP 条件是
   `if (!$todayEvents || empty($timed))`,**今天只有全天日程时整段脚本不渲染**
   (实测 hasScript=False、scrollTop=0),而 .ag-grid(0–24h) 与红线都在。
   有分时段日程时定位其实完全正确(偏差 0px)。已改成 `if (!$todayEvents)`。
2. 任务复选框提示还在 —— 真根因:`_task_check.php` 里那句无障碍文字
   `<span class="visually-hidden">` 用的类名**在 app.css 里从来没有定义过**
   (项目一直只用 .sr-only)→ 没有规则 = 普通 span = 直接可见。
   已把两个类名合并到同一条规则(clip 隐藏,仍可读屏)。
3. 名册完全错乱 —— 真根因:v29 重写平铺列表时**漏了一个 `</div>`**(闭合 .person),
   浏览器把下一张卡嵌进上一张里,实测 .person-main 只剩 24~151px(本该 ~800px)。
   `php -l` 与全部静态断言都抓不到,只有真机量 DOM 才现形。已补上闭合。
   另把 v28 引入的多列 grid 改回单列(它与 content-visibility 冲突)+窄屏动作区折行。
4. 读书慢 —— 按用户给的思路「做一个同步,然后落盘」:
   新增 weread_snapshots 表 + App\WeReadCache(按人+数据集存 JSON);
   渲染路径改走 notebooksCached()/readStatsCached()(永不联网);
   同步从"渲染之前"挪到"**响应之后**"(queueBackgroundSync + fastcgi_finish_request)。
5. 导航设置改成右上角**下拉菜单 + 四视图**(addr/general/board/engine),
   表单带 back 域 → 保存后回原来那一项。
   🔴 顺带修掉一个真 bug:`if (!$inSettings && $canWrite)` 的 endif 漂到了文件末尾,
   把 board/engine 两个视图也包进了"非设置态" → 打开时标题对、**内容是空的**。
6. 笔记列表缩略图:cover → contain + 固定高度(按比例完整显示,不裁切)。
7. 笔记段落编辑器:加 Markdown 工具条 + 斜线命令(行首打 /co → 回车补全代码块),
   两者共用同一份命令表。
8. 笔记点进来默认**阅读态**(图文混排、附件在最下方、图片四档宽度、点「编辑」才可改);
   清单块并入 Markdown —— 但**历史 list 块转写成 `- 项` 文本,老数据不丢**。
9. 笔记共享改成**逐人 + 权限**(note_shares,与 resource_shares 同构);
   可见性收敛成全站唯一的 Note::VIS_COND;迁移用一次性哨兵。
2026-10-08 10:54:43 +08:00

178 lines
8.2 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 个人笔记 · 列表(v27 建;v28 用户第 12 条重构)。
*
* 🔴 v28 第 12 条原话:「笔记列表页面,要注意大小,要么按照列表展示,要么按照块展示,
* 一行几个块,但是块大小要确定,如果显示不下,就显示简要信息,图片缩放后,放在下面。
* 这样看起来更美观。标签这个侧边栏,我感觉可以不要。」
*
* ⇒ 两处改动:
* ① **定尺寸卡片网格**(一行多个)—— 卡片高度固定,装不下就显示摘要,
* 图片等比缩放在卡片下方。原来是一列长卡片(一条占整屏),列表页很长、很难扫。
* ② **去掉左侧标签栏** —— 标签筛选改成顶部一条细筛选条(占位小得多),
* 宽度全给卡片。"可以不要"是用户的判断,照做。
*
* ⚠️ 卡片高度**固定**(CSS 里给固定高度 + 内容裁切),这是用户明确要求
* "块大小要确定"。所以卡片里不能放"会撑高的东西":
* · 正文只放**摘要**(不渲染全部块);
* · 图片只放第一张缩略图(按卡片宽度等比缩放,不会把卡片顶高)。
*
* @param array $rows 我能看到的笔记(带 blocks / tags / excerpt / is_mine)
* @param array $tagCloud 标签 + 条数
* @param string $keyword 搜索词
* @param string $tag 当前筛的标签
* @param int $totalAll 一共几条
* @param bool $showAll 是否已展开全部
* @param int $limit 列表默认条数
*/
$canWrite = \App\Auth::canWrite();
?>
<div class="page-head">
<h1>笔记</h1>
<span class="sub">共 <?= (int)$totalAll ?> 条</span>
<div class="actions">
<?php if ($canWrite): ?>
<a class="btn" href="<?= e(url('notes/form')) ?>">写笔记</a>
<?php else: ?>
<span class="link-quiet">只读账号</span>
<?php endif; ?>
</div>
</div>
<?php /* ---------------- 顶部:搜索 + 标签筛选(v28 去掉了左侧标签栏)---------------- */ ?>
<div class="card card-pad note-bar">
<form class="search note-search" method="get" action="index.php">
<input type="hidden" name="r" value="notes">
<?php if ($tag !== ''): ?>
<input type="hidden" name="tag" value="<?= e($tag) ?>">
<?php endif; ?>
<label class="sr-only" for="noteKw">搜索笔记</label>
<input class="input" id="noteKw" name="keyword" value="<?= e($keyword) ?>"
placeholder="搜标题 / 正文 / 标签" autocomplete="off">
<button class="btn btn-ghost btn-sm" type="submit">搜索</button>
<?php if ($keyword !== '' || $tag !== ''): ?>
<a class="btn btn-ghost btn-sm" href="<?= e(url('notes')) ?>">清除</a>
<?php endif; ?>
</form>
<?php if ($tagCloud): ?>
<div class="note-tags note-tagbar">
<a class="note-tag<?= $tag === '' ? ' is-on' : '' ?>" href="<?= e(url('notes')) ?>">
全部 <b><?= (int)$totalAll ?></b>
</a>
<?php foreach ($tagCloud as $t): ?>
<a class="note-tag<?= $tag === (string)$t['tag'] ? ' is-on' : '' ?>"
href="<?= e(url('notes', ['tag' => (string)$t['tag']])) ?>">
#<?= e((string)$t['tag']) ?> <b><?= (int)$t['n'] ?></b>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php if (!$rows): ?>
<div class="card card-pad">
<p class="empty-hint">
<?php if ($keyword !== '' || $tag !== ''): ?>
没有匹配的笔记。
<?php elseif ($canWrite): ?>
还没有笔记。点右上角「写笔记」随手记一条吧。
<?php else: ?>
还没有笔记。
<?php endif; ?>
</p>
</div>
<?php else: ?>
<?php /* 定尺寸网格:一行多个,卡片高度固定(用户第 12 条"块大小要确定") */ ?>
<div class="note-grid">
<?php foreach ($rows as $n): ?>
<?php
$mine = !empty($n['is_mine']);
$nid = (int)$n['id'];
$title = trim((string)$n['title']);
// 卡片上的第一张图(等比缩放在卡片下方)—— 只取一张,多了会把卡片顶高
$firstImg = '';
foreach ((array)$n['blocks'] as $b) {
if ((string)$b['kind'] === 'image' && (string)$b['content'] !== '') {
// ⚠️ 图片块内容可能是 `key|50`(v29 第 8 条:可调宽度)。
// 取 key 时要把 `|宽度` 去掉,否则拿它当 key 查会 404。
$firstImg = trim(explode('|', (string)$b['content'])[0]);
break;
}
}
?>
<article class="note-card" data-note="<?= $nid ?>">
<header class="note-head">
<span class="note-meta">
<?php
/* 🔴 v29 第 9 条:共享状态从"共享/私有"两态改成**按人数**显示 ——
共享已升级成"逐人 + 权限",只说"共享"已经说不清共享给了几个人。
$shareN 是路由一次查好的映射(别在循环里再查库)。 */
$sN = (int)($shareN[$nid] ?? 0);
?>
<?php if ($sN > 0): ?>
<span class="note-share" title="共享给 <?= $sN ?> 个人">共享 <?= $sN ?></span>
<?php else: ?>
<span class="note-private" title="只有自己可见">私有</span>
<?php endif; ?>
<span class="note-time"><?= e(substr((string)$n['updated_at'], 0, 16)) ?></span>
</span>
<?php if ($mine && $canWrite): ?>
<span class="note-ops">
<a class="note-op" href="<?= e(url('notes/form', ['id' => $nid, 'edit' => 1])) ?>"
title="编辑这条笔记">改</a>
<form method="post" action="<?= e(url('notes/destroy')) ?>" class="inline-form"
onsubmit="return confirm('删掉这条笔记?');">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= $nid ?>">
<button type="submit" class="note-op note-op-del" title="删除这条笔记">删</button>
</form>
</span>
<?php endif; ?>
</header>
<?php /* 🔴 v29 第 8 条:卡片点进去是**阅读态**(不是编辑器) */ ?>
<a class="note-hit" href="<?= e(url('notes/form', ['id' => $nid])) ?>">
<?php if ($title !== ''): ?>
<h3 class="note-title"><?= e($title) ?></h3>
<?php endif; ?>
<?php if (trim((string)($n['excerpt'] ?? '')) !== ''): ?>
<p class="note-ex"><?= e((string)$n['excerpt']) ?></p>
<?php elseif ($title === ''): ?>
<p class="note-ex note-ex-empty">(这条笔记没有正文)</p>
<?php endif; ?>
</a>
<?php /* 🔴 v29 第 6 条:「笔记,首页显示的时候,里面的图片,和行程的类似,
把图片按照比例缩放到固定的高度或者宽度,放在下面就行了。」
⇒ 卡片下方的缩略图改成**固定高度 + 等比缩放**(`object-fit: cover`
会裁切,用户看到的不再是完整的那张图;这里要的是"完整 + 整齐")。
⇒ 用 `object-fit: contain` + 固定高度:图片**完整**显示、
高度**整齐划一**,宽的那张两侧留白(比裁掉内容好,也比卡片高矮不一好)。
对比"行程"那边的做法(用户说"和行程的类似"):同一套观感。 */ ?>
<?php if ($firstImg !== ''): ?>
<div class="note-thumb">
<img src="<?= e(media_url($firstImg)) ?>" alt="" loading="lazy" decoding="async">
</div>
<?php endif; ?>
<?php if (!empty($n['tags'])): ?>
<div class="note-tags note-tags-inline">
<?php foreach ((array)$n['tags'] as $t): ?>
<a class="note-tag" href="<?= e(url('notes', ['tag' => (string)$t])) ?>">
#<?= e((string)$t) ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</article>
<?php endforeach; ?>
</div>
<?php if (!$showAll && (int)$totalAll > count($rows)): ?>
<div class="note-more">
<a class="btn btn-ghost" href="<?= e(url('notes', ['all' => 1, 'keyword' => $keyword, 'tag' => $tag])) ?>">
显示全部 <?= (int)$totalAll ?> 条
</a>
</div>
<?php endif; ?>
<?php endif; ?>