Files
laoyang 255fb8a240 网页端:保存按钮整行 + 左上角「编辑任务」+ 补回分类与循环 (#83)
用户 2026-11-05:
  「保存按钮感觉太小了。要不也像鸿蒙版一样长长的一条?」
  「最左上角也是一样,应该显示"编辑任务",而不是显示任务的标题。」
  「你倒是提醒我了,分类和重复,也就是循环,都需要加上,鸿蒙端也得加上。」

① 保存按钮 → 整行长条
   原来 `.btn-sm` 是内联宽(按文字撑),在 648px 宽的面板里又小又偏。
   ⇒ `.tk-attr-actions > .btn { width:100%; height:42px; justify-content:center }`
   实测:648 x 42,占行宽 100% ✅

② 左上角改「编辑任务」
   根因:`entry_popup.js` 的 `paintDetail()` 第 142 行永远是
     titleEl().textContent = data.title || 详情
   任务面板带 `data.panel`,它本身就是编辑面板,标题栏写任务名会让用户
   以为"这是详情页,只是恰好能改"。
   ⇒ `var headText = data.panel ? 编辑任务 : (data.title || 详情);`
   ⚠️ 其它来源(观影/旅程/日程/纪念日)**保持显示名字**:它们点开的
      确实是"这条记录的详情",显示名字才是对的。

③ 补回分类与循环
   上一轮我按「不需要额外信息」把这两项删了 —— 理解偏了:
   用户指的是"不要编辑表单里那些多余字段",不是"分类/循环不要"。
   ⇒ 补回,位置放在协作人之后(用户指定的前四项顺序不动)。
   ⚠️ `tasks/panel_save` 的 `$sd` 需要能接这两个字段 —— 见服务端那侧,
      它用 `array_key_exists` 判"传没传",所以不传时保留原值、传了就更新。

实测渲染:任务标题 → 截止 → 负责人 → 备注 → 协作人 → 分类 → 循环 ✅
2026-11-05 20:14:43 +08:00

468 lines
23 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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'] ?? '');
/* #78:协作人(共享给)。Task::find 已经带回 `shares`,直接用。 */
$tpShares = (array)($tpT['shares'] ?? []);
$tpShareIds = [];
foreach ($tpShares as $tpS) {
$tpShareIds[] = (int)($tpS['person_id'] ?? 0);
}
$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 /* #74:面板里直接改截止时间(不必点「编辑」进整页表单) */ ?>
data-tk-url-due="<?= e(url('tasks/due')) ?>"
<?php /* #76:截止/负责人/分类 三项一起保存(用户要显式「保存」按钮) */ ?>
data-tk-url-save="<?= e(url('tasks/panel_save')) ?>">
<?php /* CSRF 放在面板里(见头部第 ① 条):表单各自带一份,JS 里的按钮动作
从这里取一份 —— 两处同一个值,`csrf_token()` 天然幂等。 */ ?>
<input type="hidden" name="_csrf" value="<?= e(csrf_token()) ?>" data-tk-csrf>
<?php /* ---- 任务标题 ---- */ ?>
<div class="tk-sec">
<div class="tk-attrs">
<div class="tk-attr-row">
<span class="tk-attr-lb">任务标题</span>
<?php if ($tpCan): ?>
<input class="input" id="tkp-title" name="title" maxlength="200"
value="<?= e((string)($tpT['title'] ?? '')) ?>">
<?php else: ?>
<span class="tk-attr-val"><?= e((string)($tpT['title'] ?? '')) ?></span>
<?php endif; ?>
</div>
</div>
</div>
<?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 /* ---- 更多信息:截止 / 负责人 / 备注 / 协作人 ----
🔴🔴 #82(用户 2026-11-05):「后续内容也和鸿蒙版保持一致,不需要额外信息。
布局顺序也和鸿蒙版一致:1.任务标题 2.状态 3.看板
4.截止日期、负责人、备注、协作人 5.子任务」
⇒ 去掉「分类」和「循环」两条 —— 它们属于用户说的"额外信息"
(分类是标签、循环是只读事实,鸿蒙端面板里也没有)。
⇒ 顺序改成 截止 → 负责人 → 备注 → 协作人(与鸿蒙端逐项对齐)。
⚠️ 服务端 `tasks/panel_save` 收不到 category_id 时会**保留原值**
(`array_key_exists` 判"传没传"),所以删掉这个字段不会清空分类。 */ ?>
<div class="tk-sec">
<div class="tk-attrs">
<div class="tk-attr-row">
<span class="tk-attr-lb">截止</span>
<?php if ($tpCan): ?>
<?php
/* ⚠️ 变量名必须与 `_datetime.php` 的约定一致;
它内部一律用 `dt` 前缀的临时变量,不会踩到这里的 $tp* 或宿主页的 $t。 */
$dtName = 'due_at';
$dtValue = $tpDue;
$dtPlugin = 'antd-single';
$dtRequired = false;
$dtId = 'tkp-due';
require __DIR__ . '/../_datetime.php';
?>
<?php if ($tpDueWord !== ''): ?>
<span class="tk-due-word<?= $tpLate ? ' kb-late' : '' ?>"><?= e($tpDueWord) ?></span>
<?php endif; ?>
<?php else: ?>
<span class="<?= $tpLate ? 'kb-late' : '' ?>"><?= $tpDue !== '' ? e($tpDue) : '未设' ?></span>
<?php endif; ?>
</div>
<div class="tk-attr-row">
<span class="tk-attr-lb">负责人</span>
<?php if ($tpCan): ?>
<?php
$ppName = 'assignee_id';
$ppPeople = $tpPeople;
$ppSelected = [(int)($tpT['assignee_id'] ?? 0)];
$ppMode = 'single';
$ppOpenBtn = '更换';
$ppTitle = '谁负责这件事';
$ppEmptyText = '不指定';
$ppAllowEmpty = true;
require __DIR__ . '/../_person_picker.php';
?>
<?php else: ?>
<span><?= !empty($tpT['assignee_name'])
? e(person_name(['name' => $tpT['assignee_name'],
'my_nickname' => $tpT['assignee_nick'] ?? '']))
: '不指定' ?></span>
<?php endif; ?>
</div>
<div class="tk-attr-row tk-attr-row-top">
<span class="tk-attr-lb">备注</span>
<?php if ($tpCan): ?>
<textarea class="input" id="tkp-note" name="note" rows="2"
placeholder="补充说明…"><?= e((string)($tpT['note'] ?? '')) ?></textarea>
<?php else: ?>
<span><?= trim((string)($tpT['note'] ?? '')) !== ''
? nl2br(e((string)$tpT['note'])) : '无' ?></span>
<?php endif; ?>
</div>
<div class="tk-attr-row tk-attr-row-top">
<span class="tk-attr-lb">协作人</span>
<?php if ($tpCan): ?>
<?php
$ppName = 'shares[]';
$ppPeople = array_values(array_filter($tpPeople,
fn($pp) => (int)($pp['id'] ?? 0) !== $tpMe));
$ppSelected = array_values(array_filter($tpShareIds,
fn($sid) => $sid !== $tpMe));
$ppMode = 'multi';
$ppOpenBtn = '+ 添加人';
$ppTitle = '共享给谁';
require __DIR__ . '/../_person_picker.php';
?>
<?php else: ?>
<?php if (!$tpShares): ?>
<span>无</span>
<?php else: ?>
<span class="tk-share-chips">
<?php foreach ($tpShares as $tpS): ?>
<span class="tk-share-chip"><?= e(person_name($tpS)) ?></span>
<?php endforeach; ?>
</span>
<?php endif; ?>
<?php endif; ?>
</div>
<?php /* 🔴 #83(用户 2026-11-05):「你倒是提醒我了,分类和重复,也就是循环,
都需要加上,鸿蒙端也得加上。」
#82 里我按「不需要额外信息」把这两项删了 —— 那是我理解偏了,
用户说的是"不要编辑表单里那些额外的字段",不是"分类/循环不要"。
⇒ 补回。位置放在协作人之后:用户明确指定的前四项
(截止/负责人/备注/协作人)顺序不动,这两项作为补充排在后面。 */ ?>
<div class="tk-attr-row">
<span class="tk-attr-lb">分类</span>
<?php if ($tpCan): ?>
<select class="select" name="category_id" id="tkp-category">
<option value="0">未分类</option>
<?php foreach ($tpCats as $tpC): ?>
<?php $tpCid = (int)($tpC['id'] ?? 0); ?>
<option value="<?= $tpCid ?>"
<?= $tpCid === (int)($tpT['category_id'] ?? 0) ? ' selected' : '' ?>>
<?= e((string)($tpC['name'] ?? '')) ?>
</option>
<?php endforeach; ?>
</select>
<?php else: ?>
<span class="tk-attr-val"><?= $tpCatName !== '' ? e($tpCatName) : '未分类' ?></span>
<?php endif; ?>
</div>
<div class="tk-attr-row">
<span class="tk-attr-lb">循环</span>
<?php if ($tpCan): ?>
<select class="select" name="repeat_rule" id="tkp-repeat">
<?php foreach (\App\Task::REPEAT as $tpRk => $tpRv): ?>
<option value="<?= e($tpRk) ?>"
<?= (string)($tpT['repeat_rule'] ?? '') === (string)$tpRk ? ' selected' : '' ?>>
<?= e($tpRv) ?>
</option>
<?php endforeach; ?>
</select>
<?php else: ?>
<span class="tk-attr-val"><?= e(\App\Task::repeatWord((string)($tpT['repeat_rule'] ?? ''))) ?></span>
<?php endif; ?>
</div>
<?php if ($tpCan): ?>
<div class="tk-attr-actions">
<button type="button" class="btn btn-sm" data-tk-act="save-attrs">保存</button>
</div>
<?php endif; ?>
</div>
</div>
<?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"
enctype="multipart/form-data">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= $tpId ?>">
<input type="hidden" name="ajax_panel" value="1">
<textarea class="input" name="content" rows="2"
placeholder="说点什么… 写 @名字 可以点名"></textarea>
<?php /* 🔴 #78(用户 2026-11-05):「评论那个地方也需要可以添加附件。」
服务端**早就支持**了 —— `tasks/comment_add` 里一直有
`attach_save_files($familyId, 'task_comment', $cid, 'attach', $me);`
(index.php 那一行),只是这个表单里**没有放 file input**。
⇒ 补上。字段名 `attach[]` 与详情页、任务附件同一套。
⚠️ `<form>` 必须带 `enctype="multipart/form-data"`,
否则浏览器只提交文件名、文件本体丢失(那种"上传成功但打开是空的"最迷惑)。
⚠️ 纯附件不写字也允许(把 `required` 从 textarea 上去掉了)——
"只发一张图"是常见用法;服务端在 content 为空时会用一句
占位文案,见 comment_add 的处理。
⚠️ 用原生 `<input type=file>` 而不是拖拽框:评论是一次性提交,
拖拽框那套(`data-tk-drop-box`)是"点了立刻上传",两者语义不同。 */ ?>
<?php /* 🔴 #79(用户 2026-11-05):「评论框下面的附件也需要可以拖动添加。」
🔴 #81(同日):「评论里面拖文件上传附件,那个地方太小了。」
⇒ 原来是一个 `padding:4px 10px` 的小胶囊 + 一行小字提示,
拖动落点只有 ~26px 高,触屏上根本不好瞄,桌面上也经常拖偏。
⇒ 改成与**「任务附件」那块完全一样**的 `.upbox`:
高 96px、虚线大框、加号图标 + 说明文字。
同一个交互在一个页面里不该长两个样子,也不该一个大一个小。
⚠️ 这块的语义是"挂到评论表单、点发表才一起提交",
而上面「任务附件」是"拖上去立刻上传" —— 外观一致但行为不同,
所以说明文字要写清"随这条评论一起发出",避免用户以为已经传上去了。
⚠️ `data-cmt-drop` 仍是拖拽落点标记(task_panel.js 认它),
不要改成 `data-tk-drop-box`(那是立刻上传那条路)。 */ ?>
<div class="cmt-attach-row" data-cmt-drop>
<label class="upbox cmt-upbox" role="button" tabindex="0"
aria-label="点击选择文件,或把文件拖进来">
<input type="file" name="attach[]" multiple class="sr-only"
id="tkp-cmt-files" data-cmt-file-input>
<span class="upbox-ico" aria-hidden="true">+</span>
<span class="upbox-msg">点击选择,或把文件拖到这里</span>
<span class="cmt-upbox-sub">点「发表」时随这条评论一起发出</span>
</label>
<div class="cmt-file-list" data-cmt-file-name hidden></div>
</div>
<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);
?>