v28 第 28 批(12 条):名册分组改标签 / 资源合并为两按钮 / 首页定位与主题色 / 笔记富文本双模式
其中 4 条是**上批遗漏的真 bug**(1/4/8/9),根因已逐条定位:
1. 首页日程没定位到红线 —— 真根因:刻度区间**不含当前时刻** → $showNow=false
→ 红线**整个不渲染**,而定位脚本用同一个越界比例算目标,被 clamp 到底部。
修:把当前小时并进区间端点(现在必然落在区间内)。
2. 「看整月」→「更多日程」+ 日程主题色;3. 「手头在做」→「近期任务」、
「任务看板」→「全部任务」+ 任务主题色(注入 --hd-c,不再写死白底)。
4. 任务复选框提示还在 —— 真根因:**JS 在点击后**给状态徽标挂 title
(`badge.setAttribute('title',…)`),上批只去了静态渲染,静态断言永远抓不到。
5. 名册「分组」→「标签」(用户:一个人可能既是小学同学又是初中同学,没法分组):
新增 person_tags(每人一份、一人多标签),名册改**平铺 + 顶部标签筛选**;
🔴 迁移用**一次性哨兵**(第一版用 NOT EXISTS,实测被"用户清空标签"绕过 → 旧分组名被灌回)。
6. 名册卡片按钮加语义底色。
7. 资源四按钮 → **编辑 / 删除**两颗:三块内容进一个带页签的弹框(信息/附件/共享)。
8. 财务多选数字不变 —— 真根因:前端发 `kinds=a,b`(逗号串),服务端 `(array)` 收成
单元素 → 求交为空 → **静默回落整本账**。修:两种形状都收。
9. 微信读书偶发很慢 —— 真根因:syncNow 扫 60 本 × 每本 1~3 次网关调用(单次超时 20s),
全同步阻塞在列表页请求里。修:总时长预算 + 单次超时 8s + 自动/手动分批。
10. 新增全局可见的联想接口库(6 个实测可用来源)+ 三种新解析类型。
11. 导航右上角「设置」:默认进"添加网址 + 全部网址",四类设置收进 ?set=1 视图。
12. 笔记:段落/清单支持 Markdown(自研渲染器,**不允许原始 HTML**),
实时预览走服务端(前后端只一份规则);底部附件上传;清单多级;
列表改**定尺寸卡片网格**(溢出显示摘要、图片缩放下方);按用户要求**去掉标签侧栏**。
This commit is contained in:
+80
-1
@@ -101,6 +101,13 @@ final class Schema
|
||||
* + `note_tags`(单层标签)。参考 flomo / Obsidian / 思源,但刻意**只取轻的**:
|
||||
* 块只做 text/image/list 三种、不做块引用;标签只存字符串、不建标签表;
|
||||
* 权限默认私有、单条可一键共享给全家。详见 App\Note 头部。
|
||||
* 28 → **名册「分组」改「标签」(用户 2026-10-07 第 5 条)**:新增 `person_tags`
|
||||
* (每人一份、一人多标签、标签词存字符串)。动机是用户的原话:
|
||||
* 「一个人可能并不是只在一个分组,例如他既是我的小学同学,又是我的初中同学,
|
||||
* 就没法分组,改成标签,似乎更合理。」
|
||||
* ⚠️ `person_roster.group_id` **保留**(vCard CATEGORIES 与老代码的读点),
|
||||
* 启动时把已有分组**逐一迁成同名标签**(幂等:按 (owner,person,tag) 唯一键)。
|
||||
* 名册页从「按分栏展示」改成「平铺 + 顶部标签筛选」,拖动仍可排序。
|
||||
*/
|
||||
/**
|
||||
* 结构版本(**人读**用:日志、设置页上显示"库结构是哪一版")。
|
||||
@@ -108,7 +115,7 @@ final class Schema
|
||||
* ⚠️ 它**不再**作为"要不要跑升级"的判据 —— 见 fingerprint() 的说明。
|
||||
* 它仍然要手工维护,因为它给人看(排查时一眼知道代码到哪一版了)。
|
||||
*/
|
||||
public const VERSION = '27';
|
||||
public const VERSION = '28';
|
||||
|
||||
/**
|
||||
* 「源码指纹」:拿**本文件自身的内容**算一个哈希,作为"要不要跑升级"的判据。
|
||||
@@ -970,6 +977,37 @@ CREATE TABLE IF NOT EXISTS `note_tags` (
|
||||
CONSTRAINT `fk_nt_note` FOREIGN KEY (`note_id`) REFERENCES `notes` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='笔记标签(单层、多对多;标签词本身不建表,见 App\\Note)'
|
||||
SQL,
|
||||
// ---- 名册标签(v28 用户第 5 条)----
|
||||
// 用户原话:「名册的分组,用下来不太好,我感觉应该改成打标签,就是说,
|
||||
// 一个人,他可能并不是只在一个分组,例如,他既是我的小学同学,
|
||||
// 又是我的初中同学,就没法分组,改成标签,似乎更合理。」
|
||||
//
|
||||
// ⇒ 分组(一人一组)→ 标签(一人多标签)。**表结构为什么这样切**:
|
||||
// · `person_tags` 是**每人一份**的(owner_id = 谁的名册),
|
||||
// 与 person_roster 同口径:标签是"我和他的关系",不是这个人的属性
|
||||
// (我给他打"小学同学"不该让全家的名册都变)。
|
||||
// · `tag` 直接存字符串,**不建标签表** —— 与笔记模块同一条轻量取舍:
|
||||
// 标签是"随手打的词",不需要改名/合并/配色这些重型能力。
|
||||
// · ⚠️ `person_roster.group_id` **保留不删**:它是老库升级与 CardDAV
|
||||
// 导出的读点(vCard 的 CATEGORIES 由它派生),删掉会让"没跟上升级"
|
||||
// 的地方静默少一个字段。改成"以新表为准、老列只做兼容回填"。
|
||||
<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS `person_tags` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`family_id` INT UNSIGNED NOT NULL,
|
||||
`owner_id` INT UNSIGNED NOT NULL COMMENT '谁的名册(persons.id)—— 标签是"我和他的关系"',
|
||||
`person_id` INT UNSIGNED NOT NULL COMMENT '被打标签的人(persons.id)',
|
||||
`tag` VARCHAR(24) NOT NULL COMMENT '标签词(不含 #;同一人同一标签不重复)',
|
||||
`position` INT NOT NULL DEFAULT 10 COMMENT '同一人多个标签时的顺序',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_pt` (`owner_id`, `person_id`, `tag`),
|
||||
KEY `idx_pt_owner_tag` (`owner_id`, `tag`),
|
||||
KEY `idx_pt_person` (`person_id`),
|
||||
CONSTRAINT `fk_pt_family` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_pt_person` FOREIGN KEY (`person_id`) REFERENCES `persons` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='名册标签(v28:一个人可挂多个标签,取代"一人一组")'
|
||||
SQL,
|
||||
];
|
||||
}
|
||||
@@ -1815,6 +1853,47 @@ SQL);
|
||||
);
|
||||
});
|
||||
|
||||
// v28:把「分组」逐一迁成**同名标签**(用户第 5 条:一人一组 → 一人多标签)。
|
||||
/* 🔴🔴 必须用**一次性哨兵**,不能"每次 ensure 都尝试搬一遍"。
|
||||
*
|
||||
* 我第一版写的是「该人还没有任何标签时才搬」(NOT EXISTS 判 owner+person),
|
||||
* 想当然地以为那就能防住"删除被撤销"。**实测当场推翻了这个假设**:
|
||||
* · 用户在界面上把某人的标签**清空** → 那人身上一条标签都没有
|
||||
* → `NOT EXISTS` 成立 → 下一次 ensure **把他旧的分组名又灌回来**。
|
||||
* 症状:删掉的标签自己回来(与"移出的人又回来""纪念日关联人被老列覆盖"
|
||||
* 是同一类坑,本项目已经撞过三次)。
|
||||
*
|
||||
* ⇒ 正确做法是"这件事**只做一次**":用一个设置键当哨兵,
|
||||
* 做完了就记下,之后无论库里变成什么样都不再碰。
|
||||
* 这也符合迁移的本质 —— 它是**一次性的数据转换**,不是持续同步。
|
||||
* ⚠️ 哨兵键名带年份/版本(`person_tags_migrated_v28`),
|
||||
* 将来真有第二次迁移要另起一个键,不要复用。
|
||||
*/
|
||||
self::tryStep('名册分组搬到标签(一人一组 → 一人多标签)', static function () use ($db) {
|
||||
$flag = 'person_tags_migrated_v28';
|
||||
$has = $db->query("SELECT COUNT(*) FROM `settings` WHERE `k` = '" . $flag . "'")->fetchColumn();
|
||||
if ((int)$has > 0) {
|
||||
return; // 已经搬过了,永不再搬(见上面的说明)
|
||||
}
|
||||
if (self::hasTable($db, 'person_tags') && self::hasTable($db, 'person_groups')) {
|
||||
$db->exec(
|
||||
"INSERT IGNORE INTO `person_tags` (family_id, owner_id, person_id, tag, position)
|
||||
SELECT r.family_id, r.owner_id, r.person_id, g.name, 10
|
||||
FROM `person_roster` r
|
||||
JOIN `person_groups` g ON g.id = r.group_id
|
||||
WHERE r.group_id IS NOT NULL
|
||||
AND g.name IS NOT NULL AND g.name <> ''"
|
||||
);
|
||||
}
|
||||
// 记哨兵(settings 是**家庭级**的,用最小 family_id 当归属,与
|
||||
// schema_upgrade_warning 同一做法)
|
||||
$fid = (int)$db->query('SELECT MIN(id) FROM families')->fetchColumn();
|
||||
if ($fid > 0) {
|
||||
$db->prepare("INSERT IGNORE INTO `settings` (family_id, `k`, `v`) VALUES (?,?,?)")
|
||||
->execute([$fid, $flag, date('Y-m-d H:i:s')]);
|
||||
}
|
||||
});
|
||||
|
||||
// 被容错跳过的数据步骤记进设置,系统设置页会把它显出来 ——
|
||||
// 不然「升级没跑完」这件事只活在日志里,跟没发生一样。
|
||||
if (self::$stepErrors) {
|
||||
|
||||
Reference in New Issue
Block a user