laoyang
|
cb846d378f
|
评论附件支持拖拽 + 首页卡片同步标题 (#79)
用户 2026-11-05:
「评论框下面的附件也需要可以拖动添加。」
「首页卡片没有刷新。」
① 评论附件拖拽
给 `.cmt-attach-row` 挂 `data-cmt-drop`,由 task_panel.js 接管
dragover / dragenter / dragleave / drop。
🔴 核心技术点:`<input type=file>.files` 是**只读**的,
不能直接 `input.files = event.dataTransfer.files`(某些内核静默失败)。
必须用 DataTransfer 重建 FileList 再赋回去:
var dt = new DataTransfer();
dt.items.add(file);
input.files = dt.files; ← 这个赋值是允许的
而且**必须真的赋给 input** —— 否则只是"看起来加上了",
表单提交时浏览器一个文件都不带。
⚠️ 累加语义:用户可能先拖一张再拖一张,所以每次把 input.files
现有内容 + 新拖入的合并后写回。
⚠️ 去重:按 name+size+lastModified 判重,否则"只拖一次却传上去两张一样的"。
⚠️ dragover 必须 preventDefault(不拦的话浏览器把文件当"打开",
直接跳走、当前页面全丢)。
⚠️ 与「任务附件」那条 `[data-tk-drop-box]` 是**两条独立的路**:
那个是"点了/拖了立刻上传"(任务已存在),
这个是"挂到评论表单、点发表才一起提交"。
实测(Playwright + 真实 DataTransfer):
拖入 a.txt+b.png => count=2 ✅
再拖 c.jpg => count=3 ✅ 累加
重复拖 a.txt => count=3 ✅ 去重
FormData(form) => ["a.txt","b.png","c.jpg"] ✅ 提交时真会传
② 首页卡片同步标题
根因:`syncRow()` 原来**只管状态徽标和复选框**,代码第一行就是
`if (!d || !d.status) return;`,压根不碰标题。
而 #78 之后面板里能改标题了 ⇒ 改完点保存,卡片上还是旧标题。
改两处:
· `task_panel_json()` 返回值补 `title`(服务端直接给,不依赖 DOM 还在)
· `syncRow()` 补标题同步:`.dash-what` 的文本 + `title` 属性
(`title` 是悬停提示,不同步会出现"悬停是旧标题"的怪事),
并把 `data-fb-entry` 里的 JSON title 一并改掉
(否则"卡片上是新标题、再点开浮层显示旧标题")。
读不到 d.title 时兜底从 `#tkp-title` 输入框读。
|
2026-11-05 18:32:40 +08:00 |
|
laoyang
|
e015181fa3
|
日程里的行程条目:图标按出行方式、去掉✓、带出出发地/目的地 (#67)
用户 2026-11-05 三条:
1. 「图标应该来自里面具体的交通工具,如果是火车就应该显示火车」
原来 Calendar::month() 的 trip 派生段用 Trip::typeIcon(node_type),
typeIcon(move) 恒为 🚗 ⇒「赶路」永远显示汽车,哪怕实际坐的是火车。
改用 Trip::leadIcon(node_type, travel_mode, familyId) ——
出行方式优先、节点类型兜底,与旅行详情页行程卡同一个方法。
2. 「后面这个对号是什么意思?表示它是一个实际行程吗?我感觉没必要,直接去掉」
删掉标题拼装里的 ✓ (原来用来区分 stage=actual)。
3. 「如果行程里面有地址,也就是有出发地和目的地,应该像行程里显示的那样,
把它显示在下面,这样一点就可以导航」
这段 SQL 原来**完全没查** from_*/to_*,日历里只剩一个标题。
补 n.from_place/to_place + 四个坐标字段,输出加 from_place/to_place/
from_lng/from_lat/to_lng/to_lat。
端上:
- FbCalEvent 加 6 个字段(RawCalEvent / FbParse / FbCalStore / DemoData 同步)
- 坐标用 numStr() 转字符串(服务端 DECIMAL 可能给 number,s() 只收 string)
- FbDayEventPanel 列表卡片:时间行上面单独一行「出发地 → 目的地」,
目的地带下划线 + 日历本色,点击调 fbOpenNavigation(有坐标带坐标,
没有让 FbNav 自己地理编码)
- FbDayTimeline 全天块:一行文字「出发地 → 目的地」,不给点击
(26vp 高的块切两个热区会点不准,导航入口在详情卡里)
网页端:
- fb_entry_json() 的 rows 补「出发地」「目的地」两行
- 日历网格里的条目太窄,只显示标题,不加这两行
实测(family 1 全年):
title = [✈️ 从青岛坐飞机去哈尔滨]
from = [青岛胶东国际机场]
to = [哈尔滨太平国际机场]
to_lng= [126.241474] to_lat=[45.611938]
—— 飞机显示 ✈️、没有 ✓、地址坐标齐全
|
2026-11-05 16:12:06 +08:00 |
|
laoyang
|
bee1d077d8
|
严重修复:图片压缩参数被应用到了视频上,导致视频播不了 (#56)
用户报(2026-11-05):「你好像把 .mp4 后面也带上了问号,写成了 1600
这样的参数…你刚才没有进行图片压缩的时候,我自己播放好像还是好的。
是你更新了这个功能之后,出了这个问题。」
用户完全正确,这是我的错。
根因:media.php 只看 $imgW,没判是不是图片:
Storage::read($familyId, $key, $imgW > 0 ? $OssSizes[$imgW] : );
⇒ 视频 URL 带 ?w=1600 后,给 .mp4 对象传了
x-oss-process=image/resize,l_1600
⇒ OSS 图片处理服务对非图片返回错误内容
⇒ <video> 拿不到有效数据 ⇒ 播不了
三道防线:
1. _attachments.php(直接触发点)
原来对所有附件都传 w=1600。改为 $attKind === image 才传。
视频只传 v(版本号,破缓存用)。
2. media.php(核心防线)
$imgW 与 $isImageKey 绑定,只有图片才允许压缩。
$isImageKey 判定提前到 Storage::read 之前,
并扩到扩展名兜底(jpg/png/webp...),
视频/文档扩展名强制 false。
3. media_url()(前置防线)
生成 URL 时按扩展名过滤:非图片自动去掉 w。
这样即使调用方传错,URL 里也不会出现无效参数。
验证(模拟事故场景,URL 带 ?w=1600 请求 .mp4):
HTTP 206,Content-Type: video/mp4
无 hvc1,含 avc1
ffprobe: h264/yuv420p,2.19MB ✅ 正常
media_url 生成视频 URL:...mp4&v=2295119(无 w)
media_url 生成图片 URL:...jpg&w=1600&v=609017(有 w)
|
2026-11-05 13:14:51 +08:00 |
|
laoyang
|
2c44318a51
|
压缩只留 1600 档;视频缓存失效加版本号 (#56 #57)
一、尺寸档位(用户:「400压缩的太多了。800也不够,只保留一个1600」)
media.php 白名单从三档收到一档:
1600 => image/resize,l_1600/quality,q_88
400/800 删除。质量从 q_85 提到 q_88(只留一档就该偏清楚那侧)。
照片墙跟随改 1600,缩略图与大图共用一份缓存。
传旧 ?w=400 的链接会落到「非法值 ⇒ 原图」分支,不报错、只是不压缩。
二、视频仍显示 HEVC(用户:「他说是H.265,还是不让播呢?」)
实测服务端 15 个视频**全部 h264**,无一个 HEVC:
ffprobe 逐个查过,codec=h264,pix_fmt=yuv420p/yuvj420p
media.php 返回 Content-Type: video/mp4,body 里无 hvc1/hev1
ETag 与实际内容完全一致
问题在浏览器缓存:
Cache-Control: private, max-age=604800(7 天)
浏览器对 <video> 有自己的媒体缓存策略,不完全遵守 ETag
Range 分段缓存会把每段当独立资源
src 不变时直接复用已有媒体对象
修法:让 URL 变。media_url() 加第三个参数 $ver,
用附件 size 当版本号 → media.php?k=...&v=2295119
为什么用 size 而不是 mtime/md5:
mtime —— 对象在 OSS 上,本地拿不到可靠时间
md5 —— 要读整个文件,几百 MB 视频不可接受
size —— 转码前后必然不同(1.22MB→2.19MB),且本就在库里,零开销
照片墙已接入。其余调用点可逐步铺开。
|
2026-11-05 13:11:44 +08:00 |
|
laoyang
|
f1b09a2f41
|
v93: 首页日历本下拉能拉了 / 日程浮层只留编辑删除 / 重复日程三档 / Markdown 标题四级阶梯补齐
用户两条:
① 「新建日程,日历本是拉不下来的,是空的,好像只有一个"工作"什么的」
② 「点日程后的详情跟原来的弹框几乎一模一样,这个详情就没有意义了」
⇒ 只显示「编辑」「删除」;重复日程的删除/编辑要有三档
(当前 / 从现在到以后 / 全部)
【① 根因】`_add_modal.php` 被日程页与首页共用,HTML 一样,但自绘下拉的整套 JS
只内联在 `templates/calendar/index.php` 底部 ⇒ 首页那份没有交互,点了菜单不展开;
而按钮上是服务端渲染的默认本("工作"),所以看着"只有一个工作"。
修法:抽 `assets/js/cs_picker.js`(全站 defer、按 `[data-cs-picker]` 初始化、
`data-cs-ready` 幂等)。不扫所有 `.cs` —— 观影表单的影院下拉有自己的内联绑定,
两边都绑会"点一次开两次"。
【②】浮层自定义日程不再显示「详情」(派生条目仍只有真链接「详情」);
删除/编辑三档措辞按用户原话;`occ` 为空时只给「删除全部」,绝不让空串触发整条删除。
编辑三档的入口在 `fb_entry_json()` 把 `occ` 拼进 edit 地址(`?d=`)——
不带就只有"修改全部"一档。
【顺带修掉 v85 没做完的一半】全量套件在 v61 A9b 报红,查下去不是本轮改坏的:
CSS 只把 `##`(h4)调到 17px,`###`(h5)留在 15.5px(正文 15px,只差 0.5px)、
`####`(h6)留在 14.5px(比正文还小),而那段注释自己写的就是"三级都要调大"。
阶梯重排 h3/h4/h5/h6 = 22/20/18/17,四级两两不同号。
更值得记的是四条判据全部只盯 h4(v61 A9b、v85 B2/B2b 前身、静态断言、线上 A4),
而 h4 恰好是唯一修好的那一级 ⇒ 各加"每一级/最矮的一级"判据,并用
「只把 h6 打回 14.5px ⇒ A9b 仍绿、只有 A9b2 变红」证明新判据有独立价值。
【工具链】`_b61_falsify.py` 的 main_all 改成每个 case 起独立子进程 ——
原先同一进程连跑 10 遍探针,Playwright 的临时 profile 建/删会在同一轮里累计,
撞沙箱批量删除钩子被 SIGTERM(症状:输出戛然而止、退出码 1、一条 FAIL 都没有)。
验证:v93 正向 28/0 · v93 反向 3 处注入各自被抓 · v61 34/0 · v61 反向 9/9 ·
v85 md_heading 12/0 · v85 静态反向 8/0 · 全量流水线通过并已部署。
|
2026-10-25 14:52:43 +08:00 |
|
laoyang
|
40fd6c501c
|
v78 服务端:首页任务改成「就地处理」面板 + 新建任务搬到首页
用户原话:
「网页端,首页点击一个任务后,除了展示信息,应该有相应的功能,可以修改状态:
待办、进行中,可以挪动分组,可以拖动上传附件,可以添加评论,添加子任务等。
其目地,是为了让用户不离开首页,就能对任务进行简单的处理。」
「另外,在全部任务旁边,放一个新建任务的功能,其实就是把任务中的新建和编辑功能,
搬过来,用弹出框来处理。」
一、首页任务浮层由「只读信息」改成「可操作面板」
- 新增 templates/tasks/_panel.php:状态三段 / 看板 chips(带看板色)/
子任务(复用 _task_check.php)/ 附件拖放框(复用 _attachments.php)/ 讨论
- 新增路由 tasks/panel:只吐一块 HTML(找不到 404)
- 既有七个动作路由各加一条 ajax_panel 回包分支(状态 / 挪分组 / 新建子任务 /
评论增删 / 附件增删),没有新开写接口
- helpers.php 新增 task_panel_wanted() / task_panel_json();
fb_entry_json() 的 payload 增加 panel 字段(只有任务会给)
- 每次动作回**整块新面板**:一次动作会牵动子任务计数、附件张数、看板选中态,
逐处同步 DOM 必然漏一处
- 拖进来的附件**立刻上传**(任务已存在,不该再让用户点一次保存),
不复用 _stage_drop.php 那套「暂存 + 认领」
二、新建任务搬到首页
- 从 templates/tasks/index.php 抽出公共片段 templates/tasks/_add_modal.php
(字段与原来一字未改),首页与任务页共用同一份
- 新增 assets/js/task_add.js 管开关(两页共用,幂等绑定);
打开那一刻才 fbUpgradeRange() + fbBootAntd()(hidden 时宽高为 0,antd 会定位错乱)
- App\HomeCards 登记表里加 add => 新建任务,首页卡头与「全部任务」并排
- tasks/create 支持 back=home(白名单),新建完回首页
三、其它
- assets/js/task_panel.js:事件委托;动作后整块换面板;
顺手同步首页那张卡的状态徽标与复选框(关掉浮层不能还是旧状态);
传附件时保住用户写了一半的留言与子任务标题;取不到面板时退回只读信息
- app.css 追加面板样式(含 color-mix 的 @supports 兜底)与卡片窄到宽 2 时允许折行
验证:本地 e2e 1558/0、几何 428/0 + 660/0、v78 反向验证 11/0、
片段作用域体检 35/0、php -l 与 node --check 全过
|
2026-10-22 12:40:14 +08:00 |
|
laoyang
|
1bf56b114b
|
v49:条目浮层的「详情/编辑/删除」按**来源**分形态(三颗靠右 + 就地展开/编辑 + AJAX 删除)
用户 2026-10-14 第 2 条。用户原话:
「下面三个按钮,都放在下面的右侧…分别是详情、编辑和删除。详细用蓝色,编辑用黄色,删除用红色。
点击详细的时候,**如果这是一个日程**(正常日程里的事件、不是来自观影/旅程等模块)
→ 就**在当前页面让模态框变大**、显示更多信息。
点击编辑,也是**在当前的界面**弹出编辑日程的框,就地编辑;编辑完之后下面两个按钮:取消和保存。
点击删除,就直接把这个日程给删除。
如果当前这个日程**来自其他模块**(观影 / 旅程 / 其它)→ 点击详情就**进入那些模块**展示它。
**这种情况下,下面就没有编辑、也没有删除,就只有一个详情。**」
拍板:① 应用范围 = 首页日程 + 日程页条目(首页近期任务保持 v43 现状);
② 重复日程删除 → 弹三档范围(这一次 / 这一次及以后 / 全部)。
实现(7 个文件):
· app/helpers.php fb_entry_json() 扩契约:source('event'/'derived') / ref / del / info /
repeat / occ。判据 `kind==='custom' && ref_id>0`(**不是**"有没有 edit")。
· app/Calendar.php month() 给自定义条目带 _can_write(供 del 判定)。
· templates/_entry_popup.php 底栏重构成「右侧按钮组 + 7 颗」:详情态三颗、
编辑态两颗(取消/保存)、删除范围态两颗(取消/确认删除);另加一颗
**遮罩层里的 CSRF**(正文会被整块换掉,放正文会丢)。
· assets/js/entry_popup.js 四态状态机(detail/info/edit/delscope),按 source 决定显哪几颗。
· assets/css/app.css .fb-entry-acts(margin-left:auto 靠右)+ fb-a-detail/edit/del 三色
+ .is-wide(620→860px)。⚠️ 全是**作用域类**,不动全局 .btn-edit/.btn-danger 的语义。
· index.php 新增 calendar/event?embed=detail(只吐只读详情块);
calendar/event_del **认 X-Requested-With 回 JSON**($done() 收口,失败也回原因)。
· templates/calendar/event.php 支持 $evDetail(只吐 kv 只读块,与可写与否无关)。
本轮修掉两个真 bug(都是"藏不掉"):
① `.btn{display:inline-flex}` 是作者样式,**盖过 [hidden] 的浏览器默认**
⇒ 底栏四态切换时前面的按钮收不起来、全挤在一条上(派生条目本该只有「详情」,
实测三颗都露着)。⇒ 显式写 `.fb-entry-acts .btn[hidden]{display:none!important}`。
本项目文件前部已有同款注释(.pick-row[hidden] 等),属第 N 次同类。
② `footShow('detail')` 把三颗无条件点亮,**盖掉** paintDetail 按来源设的 hidden。
⇒ detail 组只列「详情」,且 paintDetail 里**先 footShow 再设 edit/del**(顺序写进注释)。
验证(新增 2 个套件):
· _fb_v49_entry.py(33 条真机断言:三颗靠右/配色/派生只有详情/详情就地变宽/编辑就地/
删除落库/日程页入口/重复弹范围)
· _b49_falsify.py(**10 注入 / 21 断言全抓**)
· 更新 _fb_b43_ui.py 旧断言(「详细信息」→「详情」;自定义日程的详情语义从"跳页"改"就地展开")
· familybook_deploy.sh 加 v49 静态断言段
· 全量 e2e / vCard / 几何 / 各批反向验证全绿;部署脚本 0 FAIL(含"第49批形态");
线上 7 个文件 md5 与本地逐字节一致;线上门禁诊断 13 项全 YES,临时页已删并复验 404
⚠️ 反向验证抓到一条**假绿**(值得记下来):注入「给派生条目也塞 del」时探针**没变红** ——
因为按钮显隐是 `source` **且** `del` 的多条件"与",只塞 del 在界面上显不出来。
⇒ 补一条**数据层**断言(直接读 data-fb-entry 的 del 必须为空),两层判据都在。
⚠️ 末段唯一"失败"是沙箱的安全删除门禁(SAFE_DELETE_BULK_CONFIRM_REQUIRED,按轮次累计、
本轮流水线删超 50 个文件):单独重跑 _fb_v46_carddav.py 是 **29/0**,与环境有关,与本次改动无关。
|
2026-10-14 19:05:13 +08:00 |
|
laoyang
|
075ac6bbeb
|
v44 第 44 批:界面 5 条 + 移动端 API v1 全量
界面(用户 2026-10-12 第 1~5 条)
1. 新建日程「日历本」并入 .cs 自绘下拉,颜色画进候选项,三字与下拉同行。
2. 新建日程「开始/结束」改「日期范围:」,删旧日期控件;
重复相关三行仅选重复时出现;删「(含第一次)」与「这段时间算忙碌」。
3. 条目浮层加「地点」、去掉「状态」行;首页红线按分钟定位(原只取整小时);
去掉左侧红点只留红线。
4. 任务页删掉状态说明提示(STATUS_HINT 三值清空)。
5. 新建任务:标题在前树框在后;截止时间改最新日历控件;负责人默认自己;
删冗余提示;「上面要做什么」→「任务名称」;清理括号说明。
移动端 API(用户第 6 条:导航模块除外,其余全量)
- 新增 Bearer 令牌鉴权(api_tokens 表,Schema v32),与登录口令 / DAV 口令三套分立。
- Api.php 内核(鉴权/取参/信封/分页/分发 + _method 覆写)+
ApiHandler.php 17 条资源 + ApiToken.php + api.php 入口。
- 路由:router.php / .htaccess / index.php 三处 /api/ 兜底(部署脚本自动追加线上规则)。
- 个人设置新增「App 接口令牌」卡;docs/API.md 接口文档;
docs/API-QUESTION-LIST.md 自主决策与待确认缺口清单。
验证:本地 e2e 1510/0、v43 真机 33/0、v43 反向 9/0、
v44 真机 41/0、v44 API 63/0、v44 反向 19/0;全流水线 PUSH_RC=0。
|
2026-10-12 15:31:03 +08:00 |
|
laoyang
|
8c651b43eb
|
v43 第 43 批:六条反馈(界面 5 + 条目详情浮层)
用户 2026-10-11 反馈六条,逐条定位真根因后修复:
① 纪念日「日期」多出一个控件 —— 只保留最新的那个
两层根因:(a)原生 `input[type=date]` 当初没包在 `.dt-native` 里,
v42 那条"AntD 挂上后收起原生框"的选择器根本不命中;(b)更要命的是
`.dt-native` 基类写着 `display: contents` —— 该元素**没有盒子**,
给它 position/width 完全无效 ⇒ 必须把 display 显式改回 block 才收得起来。
② 编辑纪念日把底层列表页弄没 —— 「修改」原来是整页跳 `days/edit`
(套 layout = 一张只有弹框的空白页)。改为 `?embed=1` 只吐弹框那一块,
列表页 JS 取回来替换 `#anNewCard` 并打开 —— 与「新建」同一观感。
内嵌形态标 `$anModalOnly` 以免打 standalone 标记(关闭时不再跳页)。
③ 拖图裁剪的层被弹框盖住 —— `.crop-overlay` 是 z-index:100 而 `.cal-modal`
是 200 ⇒ 裁剪层被整块盖住。提到 260(与 v41 的 .cn-modal 同一处理)。
④ 行程弹框里"参与的人"弹不出选人框 —— person_picker.js 只绑定一次,
而弹框表单是 AJAX 片段 ⇒ 里面的 `.pp` 从未被 init。三件事一起:
暴露 `window.fbInitPickers()`(幂等)+ layout 暴露 `window.fbLoadPicker()`
(脚本是按需加载的)+ 塞完片段后补调初始化。
⑤ 日程新建排版/控件/文案 —— 改走观影式 `.mv-grid`(标签对齐 + 冒号);
起止补上 `$dtPlugin='antd'`(试点正式推广到日程);标签「时间范围」→「开始 / 结束」;
删三句提示(结束留空 / 多选提醒 / 全天)。
⑥ 条目详情浮层(首页日程 / 首页近期任务 / 日程页条目)——
点条目不再直接跳页,弹浮层显示详情;「详细信息」跳该来源详情页;
「编辑」就地换成该来源的编辑表单(四处 `?embed=1` 片段)。
新增 `templates/_entry_popup.php` + `assets/js/entry_popup.js` +
`fb_entry_json()`(三个入口共用一个编码函数)。
验证:php -l 157 文件全过;div 深度 33 组合全配平;片段体检 20 片段无越界;
本地 e2e 1510/0;v43 真机 33/0;v43 反向验证 8/0;v38 36/0;v42 33/0;
b31/b32 反向验证 8/0 与 5/0;部署断言本地空跑 RC=0;完整流水线 PUSH_RC=0。
|
2026-10-12 00:24:20 +08:00 |
|
laoyang
|
2a10df2557
|
v30 后半(5 条):导入框选中反馈+单选 / 首页日程改色块 / 近期任务主题色 / 时间冲突错开 / 通知消息模块
用户 2026-10-08 第二批 5 条。
1. 导入日程选中文件后没有任何反应 + 不允许多选
`_stage_drop.php` 现在有两种模式:暂存型(附件,拖进来即刻上传)与导入型
(`$sdStage=false`,写回 input.files、随表单提交、PHP 读完即弃)。
暂存型框靠 queueDrop 画缩略图,而**导入型框选中文件后什么都不显示** ——
框里那句「点击或拖动进行上传」从头到尾不变,用户看不出"到底选上没有"。
改:app.js 新增 bindFileEcho(),只给**非暂存框**补 change 监听,
选中后框里的话换成文件名 + 图标变 ✓ + `.upbox.is-picked` 稳定态;
取消选择则还原原始文案(不是空字符串)。单选由 `$sdMultiple=false` 保证。
2. 首页「今天的日程」用色块表示,和日程保持一致
原来那套是"白底卡片 + 左边一条 3px 色线",色只占一条线,看不出是哪个日历本。
改:与日历页 `.tg-block` 同款实心色块(底色=日历本色,白字,圆角,无阴影),
left/width/top/height 全部由内联给出(冲突分列 + 真实时长)。
⚠️ `.ag-ev` 的 CSS 里去掉 `right` —— 绝对定位下 left+right+width 三者同时存在是
过约束,内联 width 会失效(冲突就不会错开了)。
3. 首页「近期任务」名称与圆点走任务模块主题色
原来用 `var(--brand)` —— 那是**当前页面**(首页)的主题色,不是任务模块的;
用户改过模块配色时两者不一致(首页砖红、任务别的色),正是他看到的"不一致"。
改:两处都走 `--tk-c`(= Module::color('tasks')),与右上角「全部任务」按钮同源。
逾期仍用红色(状态语义,不能被主题色盖掉)。
4. 日程时间冲突错开(算法借鉴 SyncCalendar)
用户指定去读 SyncCalendar 的算法。落地自
`entry/src/main/ets/common/TimelineUtil.ets` 的 build()/groupsOf():
① 划分冲突组(与组内最晚结束仍重叠就并入);② 组内贪心分列(区间图着色),
列数 = **每组各自的最大同时重叠数**(sync 原注释点名的反例:传递性重叠但
B/C 不重叠 → 只该 2 列)。落成 `App\Calendar::conflictLanes()`(PHP,判据一字未改)。
⚠️ 两处比原算法更细,都有单测钉住:
· 列数按**组**算 —— 否则一个孤立日程会被别处的三方冲突连累成 1/3 宽;
· "缺结束时间"不能一律按 30 分钟参与冲突(会与后面所有日程假冲突),
改为优先拿**同列下一条的起点**当它的视觉结束 → 因此必须**先分列再算高度**。
首页与日历页共用同一套(用户第 2 条要求"和日程保持一致")。
5. 通知消息模块(Schema v30 → v31)
不在导航显示,入口在头像下拉里、**排在个人设置之前**,名字「通知消息」;
有人在观影/旅程加我时发通知;通知出现在首页、**往年今天之前**。
新增:app/Notification.php + notifications 表(唯一键
(person_id,target_type,target_id) —— 参与人是整组替换,没有它改三次标题就收三条)+
?r=notifications{,/del,/clear} + templates/notifications/index.php + 头像下拉未读角标 +
app/helpers.php 的 fb_time_ago()。
⚠️ 三条设计判断:不进 Module::LIST(进去就会被顶栏渲染、被"模块显示与排序"当开关);
只在 person_id !== actor_id 时发(不给自己发,"通知"≠流水账);
移出参与人**撤销未读**、**保留已读**(未读是没送达的提醒,已读是看过的历史)。
验证(全部 0 失败):e2e 1478/0(+14 条新断言)、升级 153/0、vCard 40 项、城市 32/0、
几何 428/0 + 移动 680/0 + 交互 92/0、b24/b27/b28/b29 真机全绿;
冲突分列算法单测 12/0(含 SyncCalendar 点名的反例)、
新探针 _b30_ui2 16/0 + _b30_notify 16/0 + _b30_import 26/0、
反向验证 18/0(五条各自改回旧行为 → 断言全红)、
部署脚本新增 9 条守卫 + 守卫证伪 11/0。线上 11 文件 SHA256 一致,Schema v31 已升级。
|
2026-10-08 17:41:30 +08:00 |
|
laoyang
|
71a7ec35a0
|
v14: 观影/家谱/日程/旅程/资源/任务六模块统一,首页改 dashboard
用户 14 条需求一次交付(1-13 功能 + 14 工作方式)。
观影(1-4)
- 返回文案全模块统一为「返回观影」(不再有「返回列表」「返回观影记录」三种叫法)
- 观影人一律用彩色人物标签 person_chip:底色由头像主色算出来(上传时存库),
瀑布流卡片也按人显示;五处观影模板全部改用它
- 人员显示统一走 person_name()(shown → 我给他起的称呼 → 姓名)
- 影院管理页与年度报告页的上下两块用 .card + .card 统一给间距(原来紧贴成一整块)
家谱(5)
- 树根从「我」换成**辈分最高的祖先**:库画的是"树根的祖先链+后代+树根的兄弟姐妹",
根=我 时伯父/堂兄/嫂子/侄子一个都不画(12 人夹具只画出 8 人)
- focusMap 按**代数**圈定三代(不再按步数),normalizeLinks 把子女对称挂到父母双方
—— 修掉"母亲登录看不到孩子"
- 新增「看全部 / 回到我」两个按钮,放在画布外(画布整块被库接管了手势)
统一事件原语(6-9)
- 旅程:TripLog 全量迁入(3 趟旅程 / 21 段行程 / 72 张照片 / 5 个人)。
复用 events(kind='trip') + trip_details + trip_nodes(计划/执行双轨保留)
- 日程:每人可建日历本(自定义色)+ 共享只读/可写;观影与旅行是每人默认两本,
内容是**从各模块实时派生**的(不复制,所以改了观影日期日历立刻跟着变);
没有具体时分 = 全天;月视图参照群晖 Calendar
- 资源:挂在某人名下的附属信息(标题+正文+多附件),可共享给多人并设只读/读写,
默认折叠、可搜标题与正文
- 任务:看板三列 + 两级子任务 + 评论@人 + 共享 + 循环(完成时按原截止日推下一期)
- 统一附件表:四个模块的附件共用一张,下载地址不落库、一律由 media.php 现算
模块化(10-11)
- App\Module 是模块的唯一来源(加一行 = 导航条/主题色/个人设置三处自动出现)
- 个人设置里可勾选要显示哪些模块、拖动排序(关掉的模块**不会**被兜底补回来)
- 每个模块一套主题色(家庭级),只覆盖 --brand 三兄弟,不碰纸墨底色
其他(12-13)
- 新增成员表单可选所属分组(编辑表单没有:分组是"我和他的关系",只归我自己)
- 首页改 dashboard:接下来的日子 / 未来七天 / 手头在做 / 往年今日。
家人卡片从首页撤走(看人去名册)
验证(全部通过)
- e2e 542 条(新增 4 模块端到端 60+ 条)
- 桌面几何 368 / 移动端几何 520 / 家谱 12 / 交互 97 / 老库升级 123
- SQL 列名静态校验 0 处可疑(对着真实表结构)
- 反向验证 18 条新注入全部抓住
顺带修掉的既有问题
- 老库升级回填 SQL 按实际存在的列动态拼(缺列不再 1054 炸断升级)
- 全新安装时 Schema::ensure 跑在"人还没建"之前 → 补建日历本三处齐备
- 窄屏导航横向溢出(模块从 4 个加到 8 个后顶栏撑破视口)
|
2026-10-02 09:28:07 +08:00 |
|
laoyang
|
746f02581e
|
v9: 观影对齐 MovieLog —— 瀑布流/影院管理/评价评论/起止时间
- events 起止时间精确到秒(starts_at DATE→DATETIME + ends_at),_datetime.php 复用
- 影院独立成表(cinemas,地方/名称/地址/主题色 + 50 色板),movie_details 归一为 cinema_id
- 评价模型 MovieLog 同款:movie_reviews 每账号每片一条(1-10 分+正文+感受 key),
movie_review_comments 两级平铺评论(回复的回复规范化到顶层、跨评价回复拦截)
- 感受目录迁到 MovieReview::CATALOG(12 种,Emoji 11.0 老码位)
- 观影列表改瀑布流(CSS grid 海报墙),详情页五大块 + 灯箱 + 评价区
- 升级路径:v8 老数据自动归一(影院三段按 (family,place,name) 去重建行、
评价回填、中文感受反查英文 key),回填全部幂等;老库补 cinema_id 列+索引+外键
- 修三 bug:建表顺序(cinemas 必须在 movie_details 前,否则全新安装 1215)、
layout 导航用键名当路由(影院链接指向 r=cinemas)、
_datetime.php 分钟循环变量 $m 覆盖宿主模板的观影记录(编辑页简介/备注回填被清空)
|
2026-10-01 00:12:04 +08:00 |
|
laoyang
|
7e9b4520c0
|
登录居中 · 个人 slogan · 三种生日 · 称呼视角化 · 附件统一走代理
按九点反馈一次性改造:
1. 登录页「图标 + 用手机号登录你的家书」包进 .auth-head 并真正居中
(以前只加了个 class 名,没有样式,看着像做了其实没做)。
2. slogan 改成两段式:系统级 slogan 作兜底,每个人可在自己的资料里写一个,
填了就显示自己的、留空就回落到系统那句。settings 里「家庭名称」字段去掉
(只在安装时归档,不参与展示)。
3. 三个生日:身份证生日(从证件号截取,永不改动)/真实阳历/农历。
同一人填了其一即自动推出另一个(靠 birthday_source 指明填的是哪一侧,
不依赖 JS)。三者落在同一天时合并成一条提醒,否则分别提醒。
农历表不是照抄流传的那份 lunarInfo(它在 5 个年份上是错的)——
用五个独立实现按 7.3 万天多数表决重新推导,再要求 100% 重现。
4. 称呼改成「视角属性」:nicknames(family_id, viewer_id, target_id, nickname)。
「小明」不是某个人的属性——同一个人被不同人叫法不同,所以是「谁看」的属性。
旧的 persons.nickname 由 Nickname::migrateLegacy() 搬到新表,只搬一次。
5. 点自己的头像/名字直接进「我的资料」,不必再绕
「名册 → 找到自己 → 编辑资料」三步。
6. 附件(头像与其它上传)一律走 media.php 代理,不再直连 uploads/ 或云端地址:
可读性由登录态决定而不是「知道文件名」,Content-Type/缓存/下载头由服务端统一给。
uploads/ 整个目录 deny(含历史遗留备份文件),并有真实探针验证。
7. OSS 设置页加「连通性测试」(本地 / 云端两个按钮),逐步给出结论与耗时。
8. 生日输入改用「原生日期框 + 24 小时制时:分:秒 三个下拉」:
datetime-local 的格式由浏览器 UI 语言决定(中文环境渲染成「下午 03:30」,
没有秒、还容易把上下午填反)。点输入框任意一处都能弹出选择器(showPicker)。
9. 结构版本号收敛成 Schema::VERSION 一处,安装时即盖章,避免首次请求白跑升级。
验证(全部为「先注入 bug 证伪、再跑通过」):
php -l 42 个文件;e2e 149 条;老库升级专测 37 条;反向验证 23 个注入全被抓;
桌面几何 108 条(768/1280/1920/2560);移动端真机几何 212 条(360/375/390/414);
交互检查 49 条(生日整框可点、登录居中)。
|
2026-09-30 09:44:55 +08:00 |
|
laoyang
|
1669ab48d0
|
五项家庭化改造:以个人为中心 · 家谱 · 三种角色 · 附件存储可配置 · 独立页样式
1. 独立页(登录/安装/错误)套上 HTML 骨架 —— 修「首页为什么没有样式」:
新增 templates/layout-bare.php,View::render 对独立页也走完整 <head> + CSS。
2. 以个人为中心:Kinship::label() 现算称呼、Auth::homeTitle() 出「X的家书」;
同一批人在不同登录者眼里自动变成公公婆婆 / 岳父岳母(不存一次性称谓)。
3. 附件存储可配置:Storage 门面(local / 阿里云 OSS),库里只存相对键,
云端失败自动回退本地;设置页可选驱动并填 OSS 参数(照 MovieLog 的方案)。
4. 三种角色:admin / member / guest,访客只读、写操作一律 403;
accounts.role 枚举扩容,老库靠 Schema::ensure() 自动升级。
5. 家谱:只存相邻两代的边(parent / spouse),其余称呼全部推导 ——
兄弟姐妹不存边(共同父母 + birth_rank 排行推长子/次女、哥哥/妹妹),
认不清的人用虚拟人物 is_placeholder 占位,关系天然双向。
顺带修掉本地 e2e 挖出的 8 个线上级 bug(性别写死、长幼方向反、关系方向静默写反、
birth_rank 不从表单取回、隐式可空参数致响应头失效、无文件字段被误判为上传失败、
layout 变量撞车覆盖 $me、世代 0 显示成「下 0 代」)。
验证:kinship 单测 37/0 · HTTP e2e 81/0 · 老库升级专测 14/0 ·
反向验证 11/11(把每个修复改回旧写法,对应断言都会变红)。
|
2026-09-30 08:13:07 +08:00 |
|
laoyang
|
8eb19267bc
|
阶段 0 完整版:名册 + 账号 + 安装向导 + 设置(宣纸主题)
这一版把「家」的地基铺完:
功能
- 安装向导 install.php:建四张表 → 建家庭 → 建第一人(兼管理员),装完自删;
「是否已装」以数据库四张表为准,不以文件是否存在为准
- 家庭名册:成员增删改查、头像上传(居中裁方 512×512 转 JPEG、finfo 校验、
随机文件名)、生日自动算年龄与倒数天数
- 敏感信息加密存储:手机号双写(SHA-256 哈希用于登录 + AES-256-CBC 密文用于显示)、
身份证只存密文;页面上永远只显示脱敏后的 138****8000 / 前 6 后 4
- 账号体系:人与账号分离(孩子可入名册但无需密码)、开通/停用/角色、
防爆破(5 次失败锁 60 秒)、修改密码、最后一个管理员不可移除
- 设置页:家庭名称与座右铭、加密密钥状态
- 宣纸主题 UI:砖红取自图标,扁平、留白充足;移动端顶栏收紧不换行
安全
- 所有 POST 走 CSRF;输出全量 e() 转义;uploads/ 禁执行脚本
- app/ 与 templates/ 加 .htaccess 整体拒绝直连(直接请求模板会 Fatal 并
把服务器绝对路径打在页面上 —— 这是线上真实抓到过的问题)
- 已安装的 install.php 返回 404;display_errors 线上关闭
工程
- 无框架无 Composer,query-string 路由(index.php?r=模块/动作),不依赖 rewrite
- 静态资源带 mtime+size 版本号,改版后浏览器不会吃旧缓存
- 配套部署与冒烟(_tooling/,不入库):部署脚本容忍 tar 对非属主目录的
元数据回填失败;冒烟断言「状态码 + 正文无 PHP 错误 + </html> 收尾 +
无绝对路径泄露 + 断言总数下限」—— 只看状态码会把「200 但正文中间
Fatal」当成通过,这一版在线上真实抓到过一次
|
2026-09-30 00:49:53 +08:00 |
|