v72:日历本显示与管理合并成一张清单,颜色改用内置调色模态框
用户 2026-10-21 两条:
「日历设置里面日历本显示和日历管理可以放在一起」
「预制的颜色点了之后没反应……选颜色那个地方点了之后是弹出 Windows 的
一个颜色选择框,这个不好,我们把它改成内置的」
改动:
· calendar/display 整页删除,并入 calendar/calendars(保留 302 老地址)
· 新增 templates/_color_picker.php(按钮 + 值载体,表单/独立双模式)
· 新增 templates/_color_picker_modal.php(全页唯一模态框,预置色 + hex 手输)
· 共享本提到同一清单,标注归属与权限;owner 校验服务端做
· 清掉 display.php 时代遗留的死 CSS(.cal-set-page / .cal-set-book* 等)
部署中抓出并修掉两处真问题:
· 部署脚本「清掉被取代文件」清单漏了 display.php —— tar 只新增/覆盖,
git rm 掉的文件在线上永远留着,被第 7 段断言拦下(断言是对的,缺的是删除步骤)
· _color_picker.php 里残留 <input type="color" class="cp-trigger">:
原生取色控件既不能有子元素、也没有 data-cp-open ⇒
按钮回显失效(找不到 .cp-trigger-dot/hex)+ 模态框永远打不开,
用户点到的还是 Windows 取色框。改成 <button type="button"> + 色点 + hex
(v72 探针在修复前实测 2 条红,修复后 50/0)
This commit is contained in:
@@ -5240,17 +5240,29 @@ $want = array_keys($want);
|
||||
* ⚠️ 保存仍走既有的 `calendar/prefs`(一条没改)—— 它只是从"下拉里的表单"
|
||||
* 变成"这一页上的表单",提交契约、字段名、服务端逻辑完全不动。
|
||||
*/
|
||||
/**
|
||||
* 显示设置 → **已并入「日历本」页**(v72 用户 2026-10-21)。
|
||||
*
|
||||
* 用户原话:「日历设置里面日历本显示和日历管理可以放在一起啊。就是把日历本全都
|
||||
* 列出来,然后显示不显示,加个复选框就行了。」
|
||||
*
|
||||
* ⇒ v51 拆出来的 `calendar/display`(默认视图 + 显示哪些本子)与
|
||||
* `calendar/calendars`(本子管理)**合并成一页**:一张清单、每行一个复选框。
|
||||
* 那一页现在更完整(显隐 + 归属 + 权限 + 编辑/删除/共享都在一处)。
|
||||
*
|
||||
* ⚠️ 这条路由**必须留着**(302 跳过去,不是删掉):
|
||||
* · 老书签、浏览器历史里还有 `?r=calendar/display`;
|
||||
* · `_settings_menu.php` 的四行菜单里「显示设置」曾经指向它;
|
||||
* · 直接删掉的话点旧链接会走"不存在的路由"兜底,用户看到的是"设置页没了"。
|
||||
* ⇒ 与 `calendar/caldav_token` / `caldav_revoke` 那两条旧地址同样处理。
|
||||
*
|
||||
* ⚠️ 查询串要**带过去**(`view`):跳转后「返回日程」还要回到用户原来那个视图,
|
||||
* 丢了 view 就一律变成月视图(用户改完设置回到日程,发现被换成月视图)。
|
||||
*/
|
||||
case 'calendar/display':
|
||||
$familyId = (int)Auth::familyId();
|
||||
$me = (int)Auth::personId();
|
||||
$cals = Calendar::mine($familyId, $me);
|
||||
View::render('calendar/display', [
|
||||
'title' => '显示设置',
|
||||
'cals' => $cals,
|
||||
'hiddenCals' => array_keys(Calendar::hiddenCalendars($me)),
|
||||
'view' => (string)($_GET['view'] ?? Calendar::defaultView($me)),
|
||||
'anchor' => (string)($_GET['anchor'] ?? date('Y-m-d')),
|
||||
], 'calendar');
|
||||
redirect('calendar/calendars', array_filter([
|
||||
'view' => (string)($_GET['view'] ?? ''),
|
||||
], static fn($v) => $v !== ''));
|
||||
break;
|
||||
|
||||
case 'calendar/calendars':
|
||||
@@ -5291,6 +5303,15 @@ $want = array_keys($want);
|
||||
'shared' => $shared,
|
||||
'shares' => $shares,
|
||||
'editing' => $editing,
|
||||
/* 🔴 v72(用户 2026-10-21):「日历设置里面日历本显示和日历管理可以放在一起。」
|
||||
⇒ 这一页现在同时管**显隐**(原 `calendar/display` 的全部内容)
|
||||
与**本子管理**。所以这两项必须一起传进来:
|
||||
· `hiddenCals` —— 哪些本子没勾(原来是 display 页专属的数据);
|
||||
· `view` —— 回跳时保持当前视图(display 页本来就有)。
|
||||
⚠️ 别把 `calendar/display` 的取数逻辑复制一份到这里 ——
|
||||
那边已经改成 302 跳本页(见该路由),单一来源。 */
|
||||
'hiddenCals' => array_keys(Calendar::hiddenCalendars($me)),
|
||||
'view' => (string)($_GET['view'] ?? Calendar::defaultView($me)),
|
||||
'people' => Person::roster($familyId, $me, 'member', ''),
|
||||
'cdHas' => $cdHas,
|
||||
'cdPlain' => $cdPlain,
|
||||
@@ -5326,6 +5347,65 @@ $want = array_keys($want);
|
||||
redirect('calendar/calendars');
|
||||
break;
|
||||
|
||||
/**
|
||||
* 单本改色(v72 用户 2026-10-21)—— AJAX 专用。
|
||||
*
|
||||
* 用户原话:「这个颜色选择是有问题的,点了之后是没反应的,就是预制的颜色
|
||||
* 点了之后没反应的。」
|
||||
* ⇒ 真根因不是"预置色没绑事件"这么一层:日历本页把"显隐"做成了**整页一次提交**
|
||||
* (`show[]`),而改色如果也走那张大表单,一次提交就会带上 N 个同名 `color`
|
||||
* 字段 ⇒ 服务端只拿最后一个 ⇒ 出现"改了这行、那行也变了"。
|
||||
* 所以改色必须**单独一条请求**、只带一本。
|
||||
*
|
||||
* ⚠️ 与 cal_update 的区别(两个都留着,别合并):
|
||||
* · `cal_update` —— 表单提交(改名字 + 改颜色),成功/失败都 `redirect`
|
||||
* (浏览器在页面级操作,跳回去看到 flash 是对的);
|
||||
* · `cal_color` —— AJAX 提交(**只改颜色**),一律回 **JSON**
|
||||
* (前端要据此决定"保留新颜色"还是"回滚成旧的")。
|
||||
* 如果这里也 redirect,fetch 会把整页 HTML 当 json 解析失败 ⇒ 用户看到
|
||||
* "改颜色没成功"但其实已经改好了(这类"假失败"最难受)。
|
||||
*
|
||||
* 🔴 权限判据与 cal_update **一字不差**:必须是我自己的、且 kind='custom'。
|
||||
* 共享给我的本子(哪怕是"可写")也改不了 —— 颜色是主人的东西
|
||||
* (用户明确:「别人共享给我的,不让他编辑」)。
|
||||
*/
|
||||
case 'calendar/cal_color':
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$me = (int)Auth::personId();
|
||||
// ⚠️ 这里**不能**用 csrf_check():它失败时输出纯文本并 419,
|
||||
// fetch 那边 `.json()` 会抛 ⇒ 只看到"网络不通",看不到真正原因。
|
||||
$csrfOk = isset($_POST['_csrf'], $_SESSION['_csrf'])
|
||||
&& hash_equals((string)$_SESSION['_csrf'], (string)$_POST['_csrf']);
|
||||
if (!$csrfOk) {
|
||||
http_response_code(419);
|
||||
echo json_encode(['ok' => false, 'error' => '页面已过期,刷新后重试'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
if (!Auth::canWrite()) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['ok' => false, 'error' => '只读账号不能改颜色'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$color = trim((string)($_POST['color'] ?? ''));
|
||||
$cal = Calendar::find($id);
|
||||
if (!$cal || (int)$cal['owner_id'] !== $me || (string)$cal['kind'] !== 'custom') {
|
||||
http_response_code(403);
|
||||
echo json_encode(['ok' => false, 'error' => '这本日历不能改颜色'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
/* ⚠️ 颜色归一化交给 Calendar::update(内部会走 AvatarColor 的归一化并转大写)。
|
||||
但 update 是"整本更新"(name + color 一起写)⇒ 这里把**原名**带回去,
|
||||
否则改个颜色会把名字清成空串(那是个很容易漏的坑:update 的签名是
|
||||
`update($id, $name, $color)`,不是"只更新传进来的字段")。 */
|
||||
Calendar::update($id, (string)$cal['name'], $color);
|
||||
$saved = (string)Calendar::find($id)['color'];
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'color' => $saved,
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
|
||||
case 'calendar/cal_del':
|
||||
$me = (int)Auth::personId();
|
||||
Auth::requireWrite();
|
||||
|
||||
Reference in New Issue
Block a user