v24 第 2 轮:19 条清单收尾(附件预览/CalDAV/任务分类看板/全局选人/家谱/纪念日/财务)
用户 2026-10-02 提的 19 条需求,本轮完成剩下的 8 条 + CalDAV 服务端:
- ③ 附件:图片/视频给缩略图,PDF/Word/Excel 给**同尺寸**图标;可预览的在本页
弹层里看(不再新开标签页),渲染不了的点了直接下载;预览层右上角恒有下载按钮。
- ⑦b CalDAV:把「我的日历本」按 RFC 4791 只读子集分享出去(OPTIONS/PROPFIND/
REPORT/GET,写方法一律 403)。用**专用口令**(库里只存 sha256),不是登录口令。
- ⑨⑩ 任务:分类与看板都由用户自建;已完成的任务从看板消失,改在「已完成」页
按分类查看/搜索;设置入口在「新建任务」旁的 ☰。
- ⑪ 全局人员选择器:一个片段铺到观影/旅程/资源/日历共享/任务共享/财务共享;
修掉了老组件「名字和拼音都搜不动」的 bug —— .att-item 自己声明了 display:flex,
压过了浏览器 [hidden]{display:none},过滤根本没生效。
- ⑫ 家谱:头像改方形不再裁切;姻亲不在图上的卡片给「⇄ 姻亲」入口,
以 TA 为中心重画 + 「返回我的家谱」。
- ⑬ 纪念日:阴历/阳历都能设且按对应历法重复;名册里每个人的生日默认可见
(实时派生,不复制一份);全部作为全天日程进日历。
- ⑮ 财务:账本可共享,双方都能记账、都能看全部账目,资产−负债=净资产;
金额一律以「分」存,不用浮点。
验证:端到端 724 项通过 / 0 失败;几何 368、交互 97、移动端 516、家谱 24、
升级 123 全绿;SQL 列名静态校验 0 处可疑;部署 11 条线上探针全过。
This commit is contained in:
+150
-1
@@ -246,6 +246,10 @@ final class Kinship
|
||||
return ['gen' => [], 'units' => [], 'unit_of' => [], 'me' => $me];
|
||||
}
|
||||
|
||||
// ⚠️ $up / $down 是**代数标号的上下限**(clamp),不是遍历深度 ——
|
||||
// 走是走遍整个连通图的。所以同一个人可能通过两条路算出不同代数,
|
||||
// 下面取「离我最近」的那一档。
|
||||
|
||||
// ---- ① 代数:以「我」为 0 代,反复贯穿直到不再有人可加 ----
|
||||
$gen = [$me => 0];
|
||||
$seen = [$me => true];
|
||||
@@ -312,6 +316,145 @@ final class Kinship
|
||||
return ['gen' => $gen, 'units' => $units, 'unit_of' => $unitOf, 'me' => $me];
|
||||
}
|
||||
|
||||
/**
|
||||
* v24 用户第 12 条:谁的姻亲「没画在家谱上」。
|
||||
*
|
||||
* 家谱画的是「同一位祖先的血脉后代 + 各人配偶」,所以**配偶那一侧的
|
||||
* 父母/兄弟姐妹**天然不在图内(洪博为的父母不会出现在以杨家为根的树上)。
|
||||
* 用户要的是:这些人的卡片上给一个入口,点一下就以 TA 为中心重画一张,
|
||||
* 把 TA 那侧的姻亲露出来,再给个「返回」回到原图。
|
||||
*
|
||||
* 判据:**这个人有父母,但父母不在"库实际画出来的那批人"里** →
|
||||
* TA 是有独立亲缘家族的人,点一下就以 TA 为中心重画。
|
||||
*
|
||||
* ⚠️ 这里的关键是"实际画出来的" ≠ "我们遍历到的":
|
||||
* familyMap 为了算称呼会走遍**整个连通图**(连配偶的父母都拉进来),
|
||||
* 但 family-chart 只画「根的祖先链 + 根的后代 + 根的兄弟姐妹 + 这些人的配偶」——
|
||||
* **配偶的父母不在画布上**。拿遍历集合当判据,就会得出"岳父母已经在家谱里了"
|
||||
* 这种与眼睛看到的相反的结论(实测踩到)。
|
||||
*
|
||||
* @param array<int,true> $onTree 遍历到的人(用于确定根与限制范围)
|
||||
* @return array<int, array{name:string, count:int}> 人 id => 提示文案
|
||||
*/
|
||||
public static function branchOuts(array $g, array $people, array $onTree): array
|
||||
{
|
||||
// 本图的根 = 辈分最高的那个人(与 kin/tree 的取法一致)
|
||||
$rootId = 0;
|
||||
foreach (array_keys($onTree) as $id) {
|
||||
$id = (int)$id;
|
||||
if (!isset($people[$id])) {
|
||||
continue;
|
||||
}
|
||||
$hasParentOnTree = false;
|
||||
foreach (self::parentIds($g, $id) as $p) {
|
||||
if (isset($onTree[(int)$p])) {
|
||||
$hasParentOnTree = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$hasParentOnTree) {
|
||||
$rootId = $id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($rootId === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$drawn = self::drawnOf($g, $rootId, $onTree);
|
||||
|
||||
$out = [];
|
||||
foreach (array_keys($drawn) as $id) {
|
||||
$id = (int)$id;
|
||||
if ($id === $rootId || !isset($people[$id])) {
|
||||
continue;
|
||||
}
|
||||
$parents = self::parentIds($g, $id);
|
||||
if (!$parents) {
|
||||
continue; // 不知道父母的(根、待确认的占位人)没什么可展开
|
||||
}
|
||||
$outside = 0;
|
||||
foreach ($parents as $p) {
|
||||
if (!isset($drawn[(int)$p])) {
|
||||
$outside++;
|
||||
}
|
||||
}
|
||||
if ($outside === 0) {
|
||||
continue; // 父母都在画布上,这一侧已经露出来了
|
||||
}
|
||||
$out[$id] = [
|
||||
'name' => (string)($people[$id]['name'] ?? ''),
|
||||
'count' => $outside,
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* family-chart 实际会画出来的人(近似复刻它的选择规则)。
|
||||
*
|
||||
* 规则(按库的行为):
|
||||
* ① 根的**祖先链**(一直往上);
|
||||
* ② 根的**后代**(一直往下 —— 库在未设上限时不截断);
|
||||
* ③ 根的**兄弟姐妹**(与根共享父母的人);
|
||||
* ④ 以上所有人的**配偶**(夫妻并排是库的基本单位)。
|
||||
*
|
||||
* ⚠️ **配偶的父母不在其中** —— 这正是"姻亲看不到"的根源,也是本函数存在的理由。
|
||||
*
|
||||
* @param array<int,true> $inScope 只在遍历到的人里挑(脏数据不越界)
|
||||
* @return array<int,true>
|
||||
*/
|
||||
private static function drawnOf(array $g, int $rootId, array $inScope): array
|
||||
{
|
||||
$drawn = [];
|
||||
$mark = static function (int $id) use (&$drawn, $inScope): bool {
|
||||
if (!isset($inScope[$id]) || isset($drawn[$id])) {
|
||||
return false;
|
||||
}
|
||||
$drawn[$id] = true;
|
||||
return true;
|
||||
};
|
||||
$mark($rootId);
|
||||
|
||||
// ① 祖先链
|
||||
$cur = $rootId;
|
||||
for ($i = 0; $i < 12; $i++) {
|
||||
$next = 0;
|
||||
foreach (self::parentIds($g, $cur) as $p) {
|
||||
if (isset($inScope[(int)$p])) { $next = (int)$p; break; }
|
||||
}
|
||||
if ($next === 0) { break; }
|
||||
$mark($next);
|
||||
$cur = $next;
|
||||
}
|
||||
|
||||
// ③ 根的兄弟姐妹(共享父母)
|
||||
foreach (self::parentIds($g, $rootId) as $p) {
|
||||
foreach (self::childIds($g, (int)$p) as $sib) {
|
||||
$mark((int)$sib);
|
||||
}
|
||||
}
|
||||
|
||||
// ② 后代:从根出发逐层往下;④ 每加进一个人,配偶跟着进来
|
||||
$queue = array_keys($drawn);
|
||||
while ($queue) {
|
||||
$id = (int)array_shift($queue);
|
||||
foreach (self::spouseIds($g, $id) as $s) {
|
||||
if ($mark((int)$s)) { $queue[] = (int)$s; }
|
||||
}
|
||||
foreach (self::childIds($g, $id) as $c) {
|
||||
if ($mark((int)$c)) { $queue[] = (int)$c; }
|
||||
}
|
||||
}
|
||||
// 收尾再来一轮配偶(新进来的孩子要带上他们的配偶)
|
||||
foreach (array_keys($drawn) as $id) {
|
||||
foreach (self::spouseIds($g, (int)$id) as $s) {
|
||||
$mark((int)$s);
|
||||
}
|
||||
}
|
||||
return $drawn;
|
||||
}
|
||||
|
||||
/**
|
||||
* v15:把「孩子」按**父母所在的户**归堆。
|
||||
*
|
||||
@@ -566,7 +709,13 @@ final class Kinship
|
||||
return $isKnown ? ($isMale ? '表兄弟' : '表姐妹') : '表亲';
|
||||
}
|
||||
|
||||
return '亲属';
|
||||
// ⚠️ v24(用户 2026-10-02 第 1 条):这里原来兜底返回「亲属」。
|
||||
// 后果是名册里**每个没有血缘关系的人**(同事、朋友、老师…)都被贴上「亲属」——
|
||||
// 人家明明只是个联系人,叫"亲属"是错的,而且人一多满屏都是同一个词,
|
||||
// 看不出任何信息。「亲属」这个词本身也没有定义:关系推导不出来就是没关系,
|
||||
// 硬套一个称呼反而是编造。
|
||||
// 现在改为**返回空串**:没关系就不显示标签。调用方一律用 empty() 判断。
|
||||
return '';
|
||||
}
|
||||
|
||||
/** 配偶的称呼:按「我的性别」定,因为中文里丈夫/妻子是从自己视角说的 */
|
||||
|
||||
Reference in New Issue
Block a user