v41 第 41 批:流水线提速 + 12 项界面/交互开发 + DAV 彻底修复

DAV(用户「务必彻底解决」):
- 真根因是 home-set 层级错:CalDav::HOME_PATH 原为 /dav/calendars/、
  CardDav 原为 /carddav/addressbooks/,而日历本/通讯录的 href 在
  /dav/N.ics/ 与 /carddav/N.vcf/ —— home-set 下面一本都没有。
  Thunderbird 按 home-set 前缀认成员,于是「找不到日历本 / 找不到联系人」。
  改为 /dav/ 与 /carddav/(所有既有 URL 一字不变,老配置不作废)。
- 又发现并修掉 Depth:0 违反 RFC 4918:原实现无论 depth 都列出全部子集合,
  且第一条 response 是日历本(不含 current-user-principal)。改为按 depth 分流,
  Depth:0 只回资源自身、Depth:1 才列出子集合。

界面与交互:
- 观影:新建影院弹窗层级修正 + 详细地址;照片左右按钮悬停不再位移;
  弹框底栏改「确定 / 取消」。
- 旅程:新建旅程改弹窗并按观影式排版;详情页四个动作按钮按用户指定顺序与配色;
  编辑与新增行程弹窗化(单容器 + AJAX);行程时间控件改 AntD(精确到分);
  参与人默认带上自己;车次号/车牌号紧跟交通方式词。
- 资源:新建/编辑改弹窗 + 观影式排版 + 新建时即定共享人。
- 纪念日:新建改弹框 + 观影式排版。

验证:
- e2e 1510/0;v41 十二项真机 48/0;DAV 反向验证 8/0;
  部署断言空跑 337/0;模板 div 深度三形态穷举 33 组全配平。
- 线上 DAV 终验(--resolve 真实入口 + 真 DAV 口令)12/0;
  线上确认 Depth:0 恰好 1 个 response。
This commit is contained in:
2026-10-11 10:12:11 +08:00
parent f9e30bd7d8
commit 7544fb9f6a
19 changed files with 1616 additions and 342 deletions
+33
View File
@@ -233,6 +233,39 @@ if ($method === 'PROPFIND') {
if ($isHome || $bookId === 0) {
$baseHref = $isHome ? CalDav::HOME_PATH : '/dav/';
$parts = [];
// 🔴🔴 2026-10-11(本批线上实测发现的协议隐患,修之):
// RFC 4918 规定 **Depth:0 只描述「请求的那个资源本身」**,
// 而这里原来无论 depth 都把**所有日历本**一起回。
// 线上实测的坏结果:`PROPFIND /dav/ Depth:0` 的**第一条 response
// 是 `/dav/1.ics/`**(日历本自己,**不含 current-user-principal**)
// —— 严格客户端(Thunderbird / iOS)只读"与请求 URL 匹配的那条"或
// 第一条,就可能判"拿不到 principal"而中止。
// ⇒ 按 depth 分流:
// · Depth:0 → **只回 `/dav/` 自身**(principal、home-set 都在这一条里,
// 所以"把 /dav/ 当入口的老客户端"仍然一次就能往下走);
// · Depth:1 → 自身 + 各日历本。
if ($depth !== '1') {
$emit('xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav" '
. 'xmlns:card="urn:ietf:params:xml:ns:carddav"', [
'<d:response><d:href>' . CalDav::x($baseHref) . '</d:href>'
. '<d:propstat><d:prop>'
. '<d:resourcetype><d:collection/></d:resourcetype>'
. '<d:displayname>' . CalDav::x('家书日历') . '</d:displayname>'
. '<d:current-user-principal><d:href>' . CalDav::x(CalDav::PRINCIPAL_PATH)
. '</d:href></d:current-user-principal>'
. '<d:principal-URL><d:href>' . CalDav::x(CalDav::PRINCIPAL_PATH)
. '</d:href></d:principal-URL>'
. '<c:calendar-home-set><d:href>' . CalDav::x(CalDav::HOME_PATH)
. '</d:href></c:calendar-home-set>'
. '<card:addressbook-home-set><d:href>' . CalDav::x(CalDav::CARD_HOME_PATH)
. '</d:href></card:addressbook-home-set>'
. $readOnlyPriv
. '</d:prop></d:propstat></d:response>',
]);
exit;
}
foreach (CalDav::books($familyId, $me) as $c) {
$href = CalDav::hrefOf($c);
$parts[] = '<d:response><d:href>' . CalDav::x($href) . '</d:href>'