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 全过
This commit is contained in:
@@ -120,6 +120,17 @@
|
||||
<a class="btn btn-sm hd-more<?= $hcColor === '' ? ' btn-ghost' : '' ?>"
|
||||
href="<?= e($hcMore) ?>"<?= $hcColor !== '' ? ' style="--hd-c: ' . e($hcColor) . ';"' : '' ?>><?= e((string)$hcDef['more']) ?></a>
|
||||
<?php endif; ?>
|
||||
<?php /* 🔴 v78(用户 2026-10-22):「在全部任务旁边,放一个新建任务的功能,
|
||||
其实就是把任务中的新建和编辑功能,搬过来,用弹出框来处理。」
|
||||
⇒ 卡片登记表里登记了 `add` 的(目前只有「近期任务」)就在
|
||||
「全部任务」**右边**再多一颗 —— 同一套 `.hd-more` 配色,
|
||||
所以它是任务模块的主题色,不会跟旁边那颗抢眼。
|
||||
⚠️ 按钮本身只带 `data-tk-add-open`:开关逻辑在 `assets/js/task_add.js`,
|
||||
那张弹框的 HTML 在页面末尾(`tasks/_add_modal.php`)。 */ ?>
|
||||
<?php if ((string)($hcDef['add'] ?? '') !== '' && \App\Auth::canWrite()): ?>
|
||||
<button type="button" class="btn btn-sm hd-more<?= $hcColor === '' ? ' btn-ghost' : '' ?>"
|
||||
data-tk-add-open<?= $hcColor !== '' ? ' style="--hd-c: ' . e($hcColor) . ';"' : '' ?>><?= e((string)$hcDef['add']) ?></button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php require $hcTpl; ?>
|
||||
</section>
|
||||
@@ -198,3 +209,24 @@
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* ==================== 「新建任务」弹框(v78 用户 2026-10-22)====================
|
||||
用户原话:「在全部任务旁边,放一个新建任务的功能,其实就是把任务中的新建和编辑功能,
|
||||
搬过来,用弹出框来处理。」
|
||||
|
||||
⚠️ 与任务页(`templates/tasks/index.php`)**共用同一份** `tasks/_add_modal.php` ——
|
||||
两处各写一张表单,字段必然有一天不同步(`_edit_form.php` 头部记着同一条教训)。
|
||||
⚠️ `$tkAdd` 只在「近期任务」卡片真的摆在首页、且当前账号可写时才由路由准备
|
||||
(见 index.php 的 home 分支):卡片被关掉就没有入口,也就不该白跑三条查询。
|
||||
⚠️ 新建完回**首页**(`back=home`),卡片上立刻能看到刚建的那条 ——
|
||||
用户要的是"不离开首页就把事办了"。 */ ?>
|
||||
<?php if (!empty($tkAdd)): ?>
|
||||
<?php
|
||||
$tkAddBack = 'home';
|
||||
$tkAddBoards = (array)($tkAdd['boards'] ?? []);
|
||||
$tkAddCats = (array)($tkAdd['categories'] ?? []);
|
||||
$tkAddPeople = (array)($tkAdd['people'] ?? []);
|
||||
require __DIR__ . '/tasks/_add_modal.php';
|
||||
unset($tkAddBack, $tkAddBoards, $tkAddCats, $tkAddPeople, $tkAdd);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
* ⚠️ 每行前面有复选框,点了就地完成(AJAX,不跳页);表单与脚本在 home.php 末尾
|
||||
* 各一份 —— 那份**必须留着**,别跟着卡片一起删。
|
||||
* ⚠️ 标题的颜色跟着**看板色**(不是任务模块的主题色),见 v32 第 4 条那条注释。
|
||||
*
|
||||
* 🔴🔴 v78(用户 2026-10-22):「首页点击一个任务后,除了展示信息,应该有相应的功能,
|
||||
* 可以修改状态:待办、进行中,可以挪动分组,可以拖动上传附件,可以添加评论,
|
||||
* 添加子任务等。其目地,是为了让用户不离开首页,就能对任务进行简单的处理。」
|
||||
* ⇒ 每一行除 `data-fb-entry`(浮层要的基础信息)外,再给一个 `panel` 地址:
|
||||
* 浮层拿到它就把正文换成**可操作的面板**(`templates/tasks/_panel.php`),
|
||||
* 地址是 `tasks/panel?id=`,动作全走既有路由(见 helpers.php 的 task_panel_*)。
|
||||
* 面板取不到(404 / 网络)时,浮层会退回 `rows` 那份只读信息 —— 不会白屏。
|
||||
*/
|
||||
?>
|
||||
<div class="hc-body">
|
||||
@@ -35,6 +43,8 @@
|
||||
'title' => (string)$t['title'],
|
||||
'status_word' => \App\Task::statusWord((string)($t['status'] ?? 'todo')),
|
||||
'note' => (string)($t['note'] ?? ''),
|
||||
/* v78:浮层把正文换成这块面板(可就地改状态/看板/子任务/附件/评论) */
|
||||
'panel' => url('tasks/panel', ['id' => (int)$t['id']]),
|
||||
], url('tasks/show', ['id' => (int)$t['id']]),
|
||||
\App\Auth::canWrite() ? url('tasks/edit', ['id' => (int)$t['id']]) : '',
|
||||
[['截止', ($t['due_at'] ?? '') !== '' ? substr((string)$t['due_at'], 0, 16) : ''],
|
||||
|
||||
@@ -251,6 +251,13 @@ require __DIR__ . '/_entry_popup.php'; ?>
|
||||
三处入口分散在首页/日程页,按需加载的判据会在这些页面之间来回漏;
|
||||
它只有几 KB,不值得为省这点流量赌"某个页面漏加载"。 */ ?>
|
||||
<script src="<?= e(asset('/assets/js/entry_popup.js')) ?>" defer></script>
|
||||
<?php /* 🔴 v78:首页任务面板的动作脚本(状态/看板/子任务/附件/评论)。
|
||||
同样全站加载:面板是浮层的一部分,而浮层本身就挂在本布局里;
|
||||
脚本自带"面板不在就什么都不做"的判据。 */ ?>
|
||||
<script src="<?= e(asset('/assets/js/task_panel.js')) ?>" defer></script>
|
||||
<?php /* 🔴 v78:「新建任务」弹框的开关。任务页与首页共用同一张弹框,
|
||||
所以开关也只有一份 —— 两处各写一段,必然有一处忘了打开时 boot 日期控件。 */ ?>
|
||||
<script src="<?= e(asset('/assets/js/task_add.js')) ?>" defer></script>
|
||||
<?php /* 🔴 v32 第 3 条:浏览器通知(站内消息 + 日程到期提醒)。
|
||||
⚠️ **只在已登录时加载** —— 登录页轮询会一直 302 去登录页,白跑。
|
||||
同理也不在"免登录的导航展示页"上跑(那页没有会话)。
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/**
|
||||
* 「新建任务」弹框(v78 从 `tasks/index.php` 里抽出来的)。
|
||||
*
|
||||
* 为什么抽出来:用户 2026-10-22 说「在全部任务旁边,放一个新建任务的功能,
|
||||
* 其实就是把任务中的新建和编辑功能,搬过来,用弹出框来处理。」
|
||||
* ⇒ 首页也要有这张弹框。**绝不能各写一张**:
|
||||
* 两张表单的字段迟早不同步(本项目在 `_edit_form.php` 上已经栽过一次,
|
||||
* 见那份文件头部"为什么独立出来"的说明)。
|
||||
*
|
||||
* 用法(require 之前设好这几个变量):
|
||||
* $tkAddBack 新建完回哪儿:'tasks'(看板,默认)/ 'home'(首页)
|
||||
* $tkAddBoards 看板清单(Task::boards 的结果)
|
||||
* $tkAddCats 分类清单(Task::categories 的结果)
|
||||
* $tkAddPeople 名册(Person::roster 的结果)
|
||||
*
|
||||
* ⚠️ 表单字段名 / action / 契约与原来**一字未改**(服务端 `tasks/create` 收的就是这些)。
|
||||
* ⚠️ 打开/关闭由 `assets/js/task_add.js` 管(全站加载,页面里没有这张弹框就什么都不做)——
|
||||
* 两页共用一份脚本,不在这里写内联 script(内联脚本在 AJAX 换正文时不会被重跑,
|
||||
* 这个坑 v42/v47 连着踩过两次)。
|
||||
*/
|
||||
$tkAddBack = (string)($tkAddBack ?? 'tasks');
|
||||
$tkAddBoards = (array)($tkAddBoards ?? []);
|
||||
$tkAddCats = (array)($tkAddCats ?? []);
|
||||
$tkAddPeople = (array)($tkAddPeople ?? []);
|
||||
?>
|
||||
<?php if (\App\Auth::canWrite()): ?>
|
||||
<div class="cal-modal" id="taskAddCard" role="dialog" aria-modal="true"
|
||||
aria-label="新建任务" hidden>
|
||||
<div class="cal-modal-panel">
|
||||
<div class="cal-modal-head">
|
||||
<b>新建任务</b>
|
||||
<button type="button" class="cal-modal-x" data-tk-add-close aria-label="关闭">✕</button>
|
||||
</div>
|
||||
<?php /* ⚠️ enctype 必须有:本批新增了**附件**上传(用户第 6 条第 2 点
|
||||
「新建任务时,竟然没有添加附件,这个应该加上」)。
|
||||
没有它的话文件根本不会随表单发出去,而且 $_FILES 是空的。 */ ?>
|
||||
<form method="post" action="<?= e(url('tasks/create')) ?>"
|
||||
enctype="multipart/form-data" id="taskAddForm">
|
||||
<?= csrf_field() ?>
|
||||
<?php /* ⚠️ 新建后回**看板**(用户:「新建完之后刷新…看板页面」)。
|
||||
`back=tasks` / `back=home` 走服务端白名单,见 index.php 的 tasks/create。 */ ?>
|
||||
<input type="hidden" name="back" value="<?= e($tkAddBack) ?>">
|
||||
<div class="cal-modal-body">
|
||||
<?php /* 🔴 v44(用户 2026-10-12 第 5 条):「新建任务页面的排版还没有统一,
|
||||
应该把标题放在前面,树框放在后面。」
|
||||
⇒ 用观影/行程/日程那套 **`.mv-grid`**(标签 62px 右对齐 + CSS `::after` 冒号 +
|
||||
控件占满剩余宽度),标题那一行在最前、其余字段依次跟上。 */ ?>
|
||||
<div class="mv-grid">
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">任务名称<i>*</i></span>
|
||||
<input class="input" id="tk-title" name="title" required
|
||||
placeholder="如:周末大扫除">
|
||||
</div>
|
||||
<?php /* v24 第 5 条:截止时刻也走全局统一的「日期 + 时分秒」控件。
|
||||
秒这一档保留(与其它模块同一口径),默认 23:59:59 —— "这天之前做完"
|
||||
比"这天 00:00 之前"更符合直觉。
|
||||
🔴 v44(用户第 5 条):「截止时间也用我们最新的日历控件,
|
||||
记得把现在这个控件删掉。」
|
||||
⇒ 换成 AntD 的**单点** DatePicker(带时刻):
|
||||
`$dtPlugin='antd-single'`(见 app.js 的 mountAntdSingleDT)。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">截止时间</span>
|
||||
<?php $dtName = 'due_at'; $dtId = 'tk-due'; $dtValue = null;
|
||||
$dtRequired = false; $dtPlugin = 'antd-single';
|
||||
require __DIR__ . '/../_datetime.php'; ?>
|
||||
</div>
|
||||
<?php /* 🔴 v44(用户第 5 条):「负责人默认是自己。」
|
||||
⇒ 默认选中当前登录的人(原来 `$ppSelected = []`,空着)。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">负责人</span>
|
||||
<?php
|
||||
$ppName = 'assignee_id';
|
||||
$ppPeople = $tkAddPeople;
|
||||
$ppSelected = [(int)\App\Auth::personId()];
|
||||
$ppMode = 'single';
|
||||
$ppOpenBtn = '更换负责人';
|
||||
$ppTitle = '谁负责这件事';
|
||||
$ppEmptyText = '不指定';
|
||||
$ppAllowEmpty = true;
|
||||
require __DIR__ . '/../_person_picker.php';
|
||||
?>
|
||||
</div>
|
||||
<?php /* 🔴 v44(用户第 5 条):「状态下面选"已完成"的话,它只在"已完成"里出现,
|
||||
看板上不再显示,这句话也去掉。」⇒ 删掉那句 `.tip`(状态语义由徽标本身表达)。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">状态</span>
|
||||
<select class="select" id="tk-status" name="status">
|
||||
<?php foreach (\App\Task::STATUS as $sk => $sv): ?>
|
||||
<option value="<?= e($sk) ?>" <?= $sk === 'todo' ? 'selected' : '' ?>><?= e($sv) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">循环</span>
|
||||
<select class="select" id="tk-repeat" name="repeat_rule">
|
||||
<?php foreach (\App\Task::REPEAT as $rk => $rv): ?>
|
||||
<option value="<?= e($rk) ?>"><?= e($rv) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">分类</span>
|
||||
<select class="select" id="tk-cat" name="category_id">
|
||||
<option value="0">未分类</option>
|
||||
<?php foreach ($tkAddCats as $c): ?>
|
||||
<option value="<?= (int)$c['id'] ?>"><?= e((string)$c['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php /* 用户第 7 条:看板 = 任务"归属哪摊事",新建时直接选(不是顶上的切换条) */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">看板</span>
|
||||
<select class="select" id="tk-board" name="board_id">
|
||||
<?php foreach ($tkAddBoards as $b): ?>
|
||||
<option value="<?= (int)$b['id'] ?>"><?= e((string)$b['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php /* 🔴 v44(用户第 5 条):「"共享给"后面有个括号,可多选,任何人完成都算完成,
|
||||
这个括号里面的内容也去掉。」⇒ 标签只留「共享给」。
|
||||
第 8 条的同一诉求:人多时不要下拉里瞪眼找。换成弹窗 + 首字母联想。
|
||||
⚠️ 候选里去掉**自己**——「共享给自己」是空操作。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">共享给</span>
|
||||
<?php
|
||||
$ppName = 'shares[]';
|
||||
$ppPeople = array_values(array_filter($tkAddPeople,
|
||||
fn($p) => (int)$p['id'] !== (int)\App\Auth::personId()));
|
||||
$ppSelected = [];
|
||||
$ppMode = 'multi';
|
||||
$ppOpenBtn = '+ 添加人';
|
||||
$ppTitle = '共享给谁';
|
||||
require __DIR__ . '/../_person_picker.php';
|
||||
?>
|
||||
</div>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">备注</span>
|
||||
<textarea class="input" id="tk-note" name="note" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<?php /* 🔴 v37 用户第 6 条第 2 点:「新建任务时,竟然没有添加附件,这个应该加上。」
|
||||
走全站统一的「点击或拖动进行上传」框(templates/_stage_drop.php);
|
||||
字段名 `attach[]` 与详情页的附件上传**一致**。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">附件</span>
|
||||
<?php $sdName = 'attach[]'; $sdId = 'sd-tk-attach'; $sdSlot = 'photo';
|
||||
require __DIR__ . '/../_stage_drop.php'; ?>
|
||||
</div>
|
||||
</div><?php /* /.mv-grid */ ?>
|
||||
</div><?php /* /.cal-modal-body */ ?>
|
||||
<div class="cal-modal-foot">
|
||||
<span class="cal-modal-tip" id="taskAddTip"></span>
|
||||
<button class="btn btn-sm" type="submit" form="taskAddForm" id="taskAddSubmit">新建</button>
|
||||
<button class="btn btn-ghost btn-sm" type="button" data-tk-add-close>取消</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
/* ⚠️ 只清本片段自己的变量:宿主页面后面还要用 $boards / $people 这些。
|
||||
(`_edit_form.php` 头部记着"把宿主变量 unset 掉 → 子任务与评论区静默消失"那次事故。) */
|
||||
unset($tkAddBack, $tkAddBoards, $tkAddCats, $tkAddPeople,
|
||||
$dtName, $dtId, $dtValue, $dtRequired, $dtPlugin,
|
||||
$ppName, $ppPeople, $ppSelected, $ppMode, $ppOpenBtn, $ppTitle,
|
||||
$ppEmptyText, $ppAllowEmpty,
|
||||
$sdName, $sdId, $sdSlot);
|
||||
?>
|
||||
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
/**
|
||||
* 首页「近期任务」的就地面板(v78 用户 2026-10-22)。
|
||||
*
|
||||
* 用户原话:「网页端,首页点击一个任务后,除了展示信息,应该有相应的功能,
|
||||
* 可以修改状态:待办、进行中,可以挪动分组,可以拖动上传附件,可以添加评论,
|
||||
* 添加子任务等。其目地,是为了让用户不离开首页,就能对任务进行简单的处理。」
|
||||
*
|
||||
* ────────────────────────────────────────────────────────────────
|
||||
* 这个片段**只在浮层里用**(`#fbEntryBody`,由 `tasks/panel` 路由吐出)。
|
||||
* 它的每一个动作都打到**既有路由**(tasks/status、tasks/move、tasks/create、
|
||||
* tasks/comment_add、tasks/comment_del、tasks/attach_add、tasks/attach_del),
|
||||
* 一个字节的新契约都没有 —— 只是在每个表单里多带一个 `ajax_panel=1`,
|
||||
* 服务端据此把"flash + 跳转"换成"一段新面板 + 一句人话"。
|
||||
*
|
||||
* 三条纪律(每条都对应一次踩过的坑):
|
||||
* ① **每个表单自带 CSRF**:这段 HTML 是 AJAX 塞进 DOM 的,不在
|
||||
* `_entry_popup.php` 那颗 `.fb-entry-csrf` 的表单里,靠外层拿不到。
|
||||
* ② **动作按钮用 `data-tk-act` 而不是"每行一个 form"**:一条任务可能有十几条
|
||||
* 子任务与几十条评论,每行一个 form 就是名册页那个"2369 个 form"的坑
|
||||
* (密码管理器会把页面拖到几秒一帧)。这里只在真正需要输入的三处用 form:
|
||||
* 加子任务 / 发评论 / 传附件。
|
||||
* ③ **不做任何"猜"的显示**:拿不到 `cover`、拿不到人、拿不到看板就不画那一行,
|
||||
* 与 `tasks/show.php` 的口径一致(那里也是"有才显示")。
|
||||
*
|
||||
* 用法(`task_panel_json()` 里已经备好):
|
||||
* $t 任务(Task::find 的形状,含 children / comments / attachs)
|
||||
* $people 名册
|
||||
* $names @人 高亮用的名字表
|
||||
* $boards 看板清单
|
||||
* $categories 分类清单
|
||||
* $canWrite 我能不能改这一条
|
||||
* $meId 我是谁
|
||||
*
|
||||
* ⚠️ 局部变量一律 `tp` 前缀:这段会被 renderPartial 塞进浮层,宿主页面
|
||||
* (首页)里也有 `$tasks` / `$t` 这类名字,用裸名就是静默覆盖。
|
||||
*/
|
||||
$tpT = is_array($t ?? null) ? $t : [];
|
||||
$tpId = (int)($tpT['id'] ?? 0);
|
||||
if ($tpId <= 0) {
|
||||
return;
|
||||
}
|
||||
$tpBoards = (array)($boards ?? []);
|
||||
$tpCats = (array)($categories ?? []);
|
||||
$tpPeople = (array)($people ?? []);
|
||||
$tpNames = (array)($names ?? []);
|
||||
$tpCan = !empty($canWrite);
|
||||
$tpMe = (int)($meId ?? 0);
|
||||
$tpStatus = \App\Task::normalizeStatus((string)($tpT['status'] ?? 'todo'));
|
||||
$tpBoardId = (int)($tpT['board_id'] ?? 0);
|
||||
$tpKids = (array)($tpT['children'] ?? []);
|
||||
$tpAtts = (array)($tpT['attachs'] ?? []);
|
||||
$tpCmts = (array)($tpT['comments'] ?? []);
|
||||
$tpKidDone = 0;
|
||||
foreach ($tpKids as $tpKid) {
|
||||
if ((string)($tpKid['status'] ?? '') === 'done') {
|
||||
$tpKidDone++;
|
||||
}
|
||||
}
|
||||
$tpIsChild = (int)($tpT['parent_id'] ?? 0) > 0;
|
||||
/* 分类名:Task::find 不带 join,这里按 id 在清单里找(清单一次查询就够,
|
||||
比再跑一条 SQL 划算;找不到就当未分类)。 */
|
||||
$tpCatName = '';
|
||||
foreach ($tpCats as $tpCat) {
|
||||
if ((int)$tpCat['id'] === (int)($tpT['category_id'] ?? 0)) {
|
||||
$tpCatName = (string)$tpCat['name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$tpDue = (string)($tpT['due_at'] ?? '');
|
||||
$tpDueWord = \App\Task::dueWord($tpDue !== '' ? $tpDue : null);
|
||||
$tpLate = $tpDue !== '' && $tpStatus !== 'done' && \App\Task::isOverdue($tpDue);
|
||||
?>
|
||||
<div class="tk-panel" data-tk-panel="<?= $tpId ?>"
|
||||
data-tk-url-status="<?= e(url('tasks/status')) ?>"
|
||||
data-tk-url-move="<?= e(url('tasks/move')) ?>"
|
||||
data-tk-url-cmt-del="<?= e(url('tasks/comment_del')) ?>"
|
||||
data-tk-url-attach="<?= e(url('tasks/attach_add')) ?>">
|
||||
<?php /* CSRF 放在面板里(见头部第 ① 条):表单各自带一份,JS 里的按钮动作
|
||||
从这里取一份 —— 两处同一个值,`csrf_token()` 天然幂等。 */ ?>
|
||||
<input type="hidden" name="_csrf" value="<?= e(csrf_token()) ?>" data-tk-csrf>
|
||||
|
||||
<?php /* ---- 状态:三段平铺,点一下就改(不是下拉 —— 手机上少一次展开) ---- */ ?>
|
||||
<div class="tk-sec tk-sec-inline">
|
||||
<span class="tk-sec-lb">状态</span>
|
||||
<div class="tk-seg">
|
||||
<?php foreach (\App\Task::STATUS as $tpSk => $tpSv): ?>
|
||||
<?php if ($tpCan): ?>
|
||||
<button type="button"
|
||||
class="kb-st is-<?= e($tpSk) ?><?= $tpSk === $tpStatus ? ' is-on' : '' ?>"
|
||||
data-tk-act="status" data-status="<?= e($tpSk) ?>"><?= e($tpSv) ?></button>
|
||||
<?php else: ?>
|
||||
<span class="kb-st is-<?= e($tpSk) ?><?= $tpSk === $tpStatus ? ' is-on' : '' ?> is-static"><?= e($tpSv) ?></span>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php /* ---- 看板("挪动分组"就是挪到另一个看板) ---- */ ?>
|
||||
<?php if ($tpBoards): ?>
|
||||
<div class="tk-sec tk-sec-inline">
|
||||
<span class="tk-sec-lb">看板</span>
|
||||
<div class="tk-chips">
|
||||
<?php foreach ($tpBoards as $tpB): ?>
|
||||
<?php
|
||||
$tpBid = (int)$tpB['id'];
|
||||
$tpBOn = $tpBid === $tpBoardId;
|
||||
$tpBCol = \App\Task::boardColor($tpB['color'] ?? null, (string)$tpB['name']);
|
||||
?>
|
||||
<?php if ($tpCan): ?>
|
||||
<button type="button" class="tk-chip<?= $tpBOn ? ' is-on' : '' ?>"
|
||||
style="--bc: <?= e($tpBCol) ?>;"
|
||||
data-tk-act="board" data-board="<?= $tpBid ?>">
|
||||
<i class="tk-chip-dot" aria-hidden="true"></i><?= e((string)$tpB['name']) ?>
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<span class="tk-chip is-static<?= $tpBOn ? ' is-on' : '' ?>" style="--bc: <?= e($tpBCol) ?>;">
|
||||
<i class="tk-chip-dot" aria-hidden="true"></i><?= e((string)$tpB['name']) ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* ---- 只读的事实(有才显示) ---- */ ?>
|
||||
<dl class="kv tk-kv">
|
||||
<?php if ($tpDue !== ''): ?>
|
||||
<div class="kv-row"><dt>截止</dt>
|
||||
<dd class="<?= $tpLate ? 'kb-late' : '' ?>"><?= e($tpDue) ?><?= $tpDueWord !== '' ? '(' . e($tpDueWord) . ')' : '' ?></dd>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($tpT['assignee_name'])): ?>
|
||||
<div class="kv-row"><dt>负责人</dt>
|
||||
<dd><?= e(person_name(['name' => $tpT['assignee_name'], 'my_nickname' => $tpT['assignee_nick'] ?? ''])) ?></dd>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($tpCatName !== ''): ?>
|
||||
<div class="kv-row"><dt>分类</dt><dd><?= e($tpCatName) ?></dd></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($tpT['repeat_rule'])): ?>
|
||||
<div class="kv-row"><dt>循环</dt><dd><?= e(\App\Task::repeatWord((string)$tpT['repeat_rule'])) ?></dd></div>
|
||||
<?php endif; ?>
|
||||
<?php if (trim((string)($tpT['note'] ?? '')) !== ''): ?>
|
||||
<div class="kv-row"><dt>备注</dt><dd><?= nl2br(e((string)$tpT['note'])) ?></dd></div>
|
||||
<?php endif; ?>
|
||||
</dl>
|
||||
|
||||
<?php /* ---- 子任务(子任务自己不再挂子任务,与详情页同一口径) ---- */ ?>
|
||||
<?php if (!$tpIsChild): ?>
|
||||
<div class="tk-sec">
|
||||
<div class="tk-sec-h">
|
||||
子任务
|
||||
<span class="group-n"><?= (int)$tpKidDone ?>/<?= count($tpKids) ?></span>
|
||||
</div>
|
||||
<?php if ($tpKids): ?>
|
||||
<ul class="sub-list">
|
||||
<?php foreach ($tpKids as $tpKidRow): ?>
|
||||
<li class="sub-row<?= (string)$tpKidRow['status'] === 'done' ? ' is-done' : '' ?>">
|
||||
<?php
|
||||
/* 复选框用全站那一颗(`_task_check.php`)—— 但这里**不接**首页那份
|
||||
`#taskDoneForm`:面板是 AJAX 塞进来的,那份表单的监听绑在加载时
|
||||
存在的元素上。面板里的复选框由 task_panel.js 委托处理,
|
||||
表单提交目标仍是 tasks/status(只是多带 ajax_panel=1)。 */
|
||||
$tkId = (int)$tpKidRow['id'];
|
||||
$tkStatus = (string)$tpKidRow['status'];
|
||||
$tkBack = 'home';
|
||||
$tkStatic = !$tpCan;
|
||||
/* 无障碍说明:片段自己会渲染成「<label>:<状态词>」——
|
||||
所以这里只给标题,别把"子任务"三个字再拼一遍
|
||||
(否则读屏会念成「子任务子任务甲:待办」)。 */
|
||||
$tkLabel = (string)$tpKidRow['title'];
|
||||
require __DIR__ . '/../_task_check.php';
|
||||
?>
|
||||
<span class="sub-title"><?= e((string)$tpKidRow['title']) ?></span>
|
||||
<?php if ((string)($tpKidRow['due_at'] ?? '') !== ''): ?>
|
||||
<span class="node-time<?= \App\Task::isOverdue($tpKidRow['due_at'] ?? null) && $tkStatus !== 'done' ? ' kb-late' : '' ?>">
|
||||
<?= e(\App\Task::dueWord($tpKidRow['due_at'] ?? null)) ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p class="empty-hint">还没有子任务。</p>
|
||||
<?php endif; ?>
|
||||
<?php if ($tpCan): ?>
|
||||
<form method="post" action="<?= e(url('tasks/create')) ?>" class="tk-inline-form">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="parent_id" value="<?= $tpId ?>">
|
||||
<input type="hidden" name="board_id" value="<?= $tpBoardId ?>">
|
||||
<input type="hidden" name="ajax_panel" value="1">
|
||||
<input class="input" name="title" required maxlength="200" placeholder="加一项子任务,回车即可">
|
||||
<button class="btn btn-sm" type="submit">加上</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* ---- 附件:缩略图墙复用全站那一份;拖框是"拖进来立刻传" ---- */ ?>
|
||||
<div class="tk-sec">
|
||||
<div class="tk-sec-h">附件 <span class="group-n"><?= count($tpAtts) ?></span></div>
|
||||
<?php if ($tpAtts): ?>
|
||||
<?php
|
||||
/* ⚠️ 删除表单走 `_attachments.php`(全站唯一画法),多带一个 `ajax_panel`
|
||||
让服务端回"新面板"而不是跳详情页 —— 由 task_panel.js 拦下提交。 */
|
||||
$attRows = $tpAtts;
|
||||
$attCanEdit = $tpCan;
|
||||
$attDelUrl = 'tasks/attach_del';
|
||||
$attDelField = 'id';
|
||||
$attExtra = ['ajax_panel' => '1'];
|
||||
require __DIR__ . '/../_attachments.php';
|
||||
?>
|
||||
<?php else: ?>
|
||||
<p class="empty-hint">还没有附件。</p>
|
||||
<?php endif; ?>
|
||||
<?php if ($tpCan): ?>
|
||||
<?php /* 🔴 这里**有意不用** `_stage_drop.php`:那个片段是"拖进来先落暂存区、
|
||||
点保存时才记到这条上",因为它服务的都是"表单还没提交"的新建场景。
|
||||
而这一条任务**已经存在**,用户拖进来就该立刻挂上去 ——
|
||||
多一次"再点上传"是多余的一步(用户原话:「可以拖动上传附件」)。
|
||||
框的样子与那句话仍然一模一样(`.upfield / .upbox`)。 */ ?>
|
||||
<div class="upfield tk-drop" data-tk-drop>
|
||||
<div class="upbox" data-tk-drop-box role="button" tabindex="0"
|
||||
aria-label="点击或拖动进行上传">
|
||||
<span class="upbox-ico" aria-hidden="true">+</span>
|
||||
<span class="upbox-msg">点击或拖动进行上传</span>
|
||||
</div>
|
||||
<input class="sr-only" type="file" multiple data-tk-drop-input
|
||||
aria-label="选择要上传的附件">
|
||||
<div class="stage-list" data-tk-drop-list hidden></div>
|
||||
<span class="stage-drop-tip" data-tk-drop-tip hidden></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php /* ---- 讨论 ---- */ ?>
|
||||
<div class="tk-sec">
|
||||
<div class="tk-sec-h">讨论 <span class="group-n"><?= count($tpCmts) ?></span></div>
|
||||
<?php if ($tpCmts): ?>
|
||||
<ul class="cmt-list">
|
||||
<?php foreach ($tpCmts as $tpCmt): ?>
|
||||
<li class="cmt-row">
|
||||
<div class="cmt-who">
|
||||
<?= person_chip($tpCmt) ?>
|
||||
<span class="cmt-time"><?= e((string)$tpCmt['created_at']) ?></span>
|
||||
<?php if ((int)$tpCmt['person_id'] === $tpMe && $tpCan): ?>
|
||||
<?php /* `btn-inline` 是"长得像链接的按钮"(见 app.css 注释):
|
||||
少了它,<button> 会带上浏览器的灰底与内边距。 */ ?>
|
||||
<button type="button" class="link-quiet btn-inline cmt-x"
|
||||
data-tk-act="cmt-del" data-cmt="<?= (int)$tpCmt['id'] ?>">删除</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="cmt-body"><?= \App\Task::commentHtml((string)$tpCmt['content'], $tpNames) ?></div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p class="empty-hint">还没有留言。写 @名字 可以点名。</p>
|
||||
<?php endif; ?>
|
||||
<?php if ($tpCan): ?>
|
||||
<form method="post" action="<?= e(url('tasks/comment_add')) ?>" class="cmt-form">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="id" value="<?= $tpId ?>">
|
||||
<input type="hidden" name="ajax_panel" value="1">
|
||||
<textarea class="input" name="content" rows="2" required
|
||||
placeholder="说点什么… 写 @名字 可以点名"></textarea>
|
||||
<div class="btn-row"><button class="btn btn-sm" type="submit">发表</button></div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
/* ⚠️ 只清本片段自己造的变量:`$t` / `$people` / `$boards` 这些是宿主的大局变量,
|
||||
清掉之后宿主后半段会静默拿不到它们(`_edit_form.php` 头部记着这次事故)。 */
|
||||
unset($tpT, $tpBoards, $tpCats, $tpPeople, $tpNames, $tpCan, $tpMe, $tpStatus,
|
||||
$tpBoardId, $tpKids, $tpAtts, $tpCmts, $tpKidDone, $tpIsChild, $tpCatName,
|
||||
$tpDue, $tpDueWord, $tpLate, $tpId,
|
||||
$tpKid, $tpCat, $tpB, $tpBid, $tpBOn, $tpBCol, $tpKidRow, $tpCmt,
|
||||
$attRows, $attCanEdit, $attDelUrl, $attDelField, $attExtra);
|
||||
?>
|
||||
+15
-177
@@ -57,7 +57,7 @@ $lastColIdx = count($colList) - 1;
|
||||
</span>
|
||||
<div class="actions">
|
||||
<?php if ($canWrite): ?>
|
||||
<button class="btn" type="button" id="taskAddBtn">新建任务</button>
|
||||
<button class="btn" type="button" data-tk-add-open>新建任务</button>
|
||||
<?php else: ?>
|
||||
<span class="link-quiet">只读账号</span>
|
||||
<?php endif; ?>
|
||||
@@ -65,158 +65,17 @@ $lastColIdx = count($colList) - 1;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($canWrite): ?>
|
||||
<?php /* 🔴🔴 v37 用户第 6 条第 3 点:「新建任务也和新建日程一样,新建时弹出一个框,
|
||||
新建完之后刷新新建任务的列表、看板页面。」
|
||||
⇒ 从"页面上展开的一张卡片"改成**浮层弹框**(与日程弹框同一套形态 / 类名),
|
||||
提交后照旧 302 回看板刷新 —— 用户要的"刷新看板页面"就是这样。
|
||||
⚠️ 表单字段本身**一个字没动**(除了新加的附件框),服务端契约照旧。 */ ?>
|
||||
<div class="cal-modal" id="taskAddCard" role="dialog" aria-modal="true"
|
||||
aria-label="新建任务" hidden>
|
||||
<div class="cal-modal-panel">
|
||||
<div class="cal-modal-head">
|
||||
<b>新建任务</b>
|
||||
<button type="button" class="cal-modal-x" id="taskAddX" aria-label="关闭">✕</button>
|
||||
</div>
|
||||
<?php /* ⚠️ enctype 必须有:本批新增了**附件**上传(用户第 6 条第 2 点
|
||||
「新建任务时,竟然没有添加附件,这个应该加上」)。
|
||||
没有它的话文件根本不会随表单发出去,而且 $_FILES 是空的。 */ ?>
|
||||
<form method="post" action="<?= e(url('tasks/create')) ?>"
|
||||
enctype="multipart/form-data" id="taskAddForm">
|
||||
<?= csrf_field() ?>
|
||||
<?php /* ⚠️ 新建后回**看板**(用户:「新建完之后刷新…看板页面」)。
|
||||
原来服务端一律 redirect('tasks/show'),新建完会跳去详情页。
|
||||
`back=tasks` 走白名单。 */ ?>
|
||||
<input type="hidden" name="back" value="tasks">
|
||||
<div class="cal-modal-body">
|
||||
<?php /* 🔴 v44(用户 2026-10-12 第 5 条):「新建任务页面的排版还没有统一,
|
||||
应该把标题放在前面,树框放在后面。」
|
||||
⇒ 用观影/行程/日程那套 **`.mv-grid`**(标签 62px 右对齐 + CSS `::after` 冒号 +
|
||||
控件占满剩余宽度),标题那一行在最前、其余字段依次跟上 ——
|
||||
"标题在前、后面的框跟上"就是这套排版的自然顺序。 */ ?>
|
||||
<div class="mv-grid">
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">任务名称<i>*</i></span>
|
||||
<input class="input" id="tk-title" name="title" required
|
||||
placeholder="如:周末大扫除">
|
||||
</div>
|
||||
<?php /* v24 第 5 条:截止时刻也走全局统一的「日期 + 时分秒」控件。
|
||||
秒这一档保留(与其它模块同一口径),默认 23:59:59 —— "这天之前做完"
|
||||
比"这天 00:00 之前"更符合直觉。
|
||||
🔴 v44(用户第 5 条):「截止时间也用我们最新的日历控件,
|
||||
记得把现在这个控件删掉。」
|
||||
⇒ 换成 AntD 的**单点** DatePicker(带时刻):
|
||||
`$dtPlugin='antd-single'`(见 app.js 的 mountAntdSingleDT)。
|
||||
原生那几档下拉由 CSS **真删掉**(不是 clip 藏起来)。
|
||||
标签去掉那个括号(见下一条)。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">截止时间</span>
|
||||
<?php $dtName = 'due_at'; $dtId = 'tk-due'; $dtValue = null;
|
||||
$dtRequired = false; $dtPlugin = 'antd-single';
|
||||
require __DIR__ . '/../_datetime.php'; ?>
|
||||
</div>
|
||||
<?php /* 🔴 v44(用户第 5 条):「负责人默认是自己。」
|
||||
⇒ 默认选中当前登录的人(原来 `$ppSelected = []`,空着)。
|
||||
⚠️ 用 `Auth::personId()` 取"我",与后端"负责人为空"的兜底一致。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">负责人</span>
|
||||
<?php
|
||||
$ppName = 'assignee_id';
|
||||
$ppPeople = $people;
|
||||
$ppSelected = [(int)\App\Auth::personId()];
|
||||
$ppMode = 'single';
|
||||
$ppOpenBtn = '更换负责人';
|
||||
$ppTitle = '谁负责这件事';
|
||||
$ppEmptyText = '不指定';
|
||||
$ppAllowEmpty = true;
|
||||
require __DIR__ . '/../_person_picker.php';
|
||||
?>
|
||||
</div>
|
||||
<?php /* 🔴 v44(用户第 5 条):「状态下面选"已完成"的话,它只在"已完成"里出现,
|
||||
看板上不再显示,这句话也去掉。」
|
||||
⇒ 删掉那句 `.tip`(状态语义由状态徽标本身表达)。
|
||||
⚠️ 用户同条要求「任务页面,任务下面有一行字…这个提示不要了」——
|
||||
那是**看板顶部**那块「状态说明」(见上方 legend 那段)。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">状态</span>
|
||||
<select class="select" id="tk-status" name="status">
|
||||
<?php foreach (\App\Task::STATUS as $sk => $sv): ?>
|
||||
<option value="<?= e($sk) ?>" <?= $sk === 'todo' ? 'selected' : '' ?>><?= e($sv) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">循环</span>
|
||||
<select class="select" id="tk-repeat" name="repeat_rule">
|
||||
<?php foreach (\App\Task::REPEAT as $rk => $rv): ?>
|
||||
<option value="<?= e($rk) ?>"><?= e($rv) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">分类</span>
|
||||
<select class="select" id="tk-cat" name="category_id">
|
||||
<option value="0">未分类</option>
|
||||
<?php foreach ($categories as $c): ?>
|
||||
<option value="<?= (int)$c['id'] ?>"><?= e((string)$c['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php /* 用户第 7 条:看板 = 任务"归属哪摊事",新建时直接选(不再是顶上的切换条) */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">看板</span>
|
||||
<select class="select" id="tk-board" name="board_id">
|
||||
<?php foreach ($boards as $b): ?>
|
||||
<option value="<?= (int)$b['id'] ?>"><?= e((string)$b['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php /* 🔴 v44(用户第 5 条):「"共享给"后面有个括号,可多选,任何人完成都算完成,
|
||||
这个括号里面的内容也去掉。」⇒ 标签只留「共享给」。
|
||||
第 8 条的同一诉求:人多时不要下拉里瞪眼找。换成弹窗 + 首字母联想。
|
||||
⚠️ 候选里去掉**自己**——「共享给自己」是空操作;组件没有"排除项",
|
||||
所以在传进去之前先过滤掉。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">共享给</span>
|
||||
<?php
|
||||
$ppName = 'shares[]';
|
||||
$ppPeople = array_values(array_filter($people,
|
||||
fn($p) => (int)$p['id'] !== (int)\App\Auth::personId()));
|
||||
$ppSelected = [];
|
||||
$ppMode = 'multi';
|
||||
$ppOpenBtn = '+ 添加人';
|
||||
$ppTitle = '共享给谁';
|
||||
require __DIR__ . '/../_person_picker.php';
|
||||
?>
|
||||
</div>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">备注</span>
|
||||
<textarea class="input" id="tk-note" name="note" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<?php /* 🔴 v37 用户第 6 条第 2 点:「新建任务时,竟然没有添加附件,
|
||||
这个应该加上。」
|
||||
⚠️ 走全站统一的「点击或拖动进行上传」框(templates/_stage_drop.php);
|
||||
字段名 `attach[]` 与详情页的附件上传**一致** ——
|
||||
服务端 attach_save_files(..., 'attach', ...) 零改动就能收。
|
||||
⚠️ 拖进来的走暂存(stage_token[]),随表单一起认领;
|
||||
直接点选走 $_FILES 路径。两条路服务端都接(见 attach_save_files)。 */ ?>
|
||||
<div class="mv-row">
|
||||
<span class="mv-lb">附件</span>
|
||||
<?php $sdName = 'attach[]'; $sdId = 'sd-tk-attach'; $sdSlot = 'photo';
|
||||
require __DIR__ . '/../_stage_drop.php'; ?>
|
||||
</div>
|
||||
</div><?php /* /.mv-grid */ ?>
|
||||
</div><?php /* /.cal-modal-body */ ?>
|
||||
<div class="cal-modal-foot">
|
||||
<span class="cal-modal-tip" id="taskAddTip"></span>
|
||||
<button class="btn btn-sm" type="submit" form="taskAddForm" id="taskAddSubmit">新建</button>
|
||||
<button class="btn btn-ghost btn-sm" type="button" id="taskAddCancel">取消</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php /* 🔴 v78(用户 2026-10-22):新建任务弹框与**首页**共用同一份
|
||||
`templates/tasks/_add_modal.php`(不许各写一张 —— 字段迟早不同步)。
|
||||
⚠️ 打开/关闭的脚本也搬到了 `assets/js/task_add.js`(两页共用一份)。
|
||||
⚠️ 这里把本页已有的 `$boards` / `$categories` / `$people` 原样传进去;
|
||||
新建完回看板(`back=tasks`)。 */
|
||||
$tkAddBack = 'tasks';
|
||||
$tkAddBoards = $boards;
|
||||
$tkAddCats = $categories;
|
||||
$tkAddPeople = $people;
|
||||
require __DIR__ . '/_add_modal.php';
|
||||
unset($tkAddBack, $tkAddBoards, $tkAddCats, $tkAddPeople); ?>
|
||||
|
||||
<?php /* 拖动的提交目标:JS 把 id / 目标**看板** / 参照顺序填进来再 submit。
|
||||
放在看板**外面**,避免被任何一张卡片的 form 套住。 */ ?>
|
||||
@@ -375,30 +234,9 @@ $lastColIdx = count($colList) - 1;
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
/* ---- "新建任务"弹框(v37:从内联卡片改成浮层)----
|
||||
⚠️ 不再 scrollIntoView —— 那是"卡片长在页面上"时代才需要的动作;
|
||||
浮层固定在视口中央,底下的看板一动不动。 */
|
||||
var addBtn = document.getElementById('taskAddBtn');
|
||||
var addCard = document.getElementById('taskAddCard');
|
||||
var addX = document.getElementById('taskAddX');
|
||||
var addCancel = document.getElementById('taskAddCancel');
|
||||
function openAdd() {
|
||||
if (!addCard) { return; }
|
||||
addCard.hidden = false;
|
||||
var t = document.getElementById('tk-title');
|
||||
if (t) { t.focus(); }
|
||||
}
|
||||
function closeAdd() { if (addCard) { addCard.hidden = true; } }
|
||||
if (addBtn && addCard) { addBtn.addEventListener('click', openAdd); }
|
||||
if (addX) { addX.addEventListener('click', closeAdd); }
|
||||
if (addCancel) { addCancel.addEventListener('click', closeAdd); }
|
||||
/* 点遮罩关(点面板内不关)/ Esc 关 —— 与站内其它浮层同一套行为。 */
|
||||
if (addCard) {
|
||||
addCard.addEventListener('click', function (e) { if (e.target === addCard) { closeAdd(); } });
|
||||
}
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && addCard && !addCard.hidden) { closeAdd(); }
|
||||
});
|
||||
/* 🔴 v78:「新建任务」弹框的开关搬到了 `assets/js/task_add.js`
|
||||
—— 首页那张卡也要开同一张弹框,两处各写一份必然有一处忘了 `fbBootAntd`。
|
||||
本文件从这一行往下只剩**看板**自己的交互(搬卡 / 状态 / 拖动)。 */
|
||||
|
||||
/* ---- 左右搬(不需要拖也能用)----
|
||||
⚠️ 列的集合**从 DOM 现取**,不要写死数组:看板是用户自己建的,
|
||||
|
||||
Reference in New Issue
Block a user