Files
FamilyBook/templates/home/_card_tasks.php
T
laoyang 4941e9db9c fix(home): 从浮层把任务标成完成时该行不消失(只修了复选框那条路)(#20)
用户第二次提同一条需求。核了线上代码:4 文件 md5 与本地一致、mtime 18:14,
部署没问题;复跑原判据仍 20/0 全绿。判据绿 + 用户能复现 ⇒ 判据有盲区。

盲区:首页把任务标成完成有**两个入口**
  ① 每行前面那颗复选框 —— home.php 末尾的脚本(上一版已修)
  ② 点整行弹浮层 → 面板里「状态」三段按钮 → assets/js/task_panel.js 的 syncRow()
上一版只改了 ①。syncRow() 只改徽章与复选框的 aria 状态,**从不删行**,
所以从面板点「已完成」后行留在原地,只把徽标换成「已完成」。
用户点的就是 ②,而原判据 grep 全文没有 panel / 浮层 / data-fb-entry —— 完全没测这条路。

改法:
- 把删行逻辑抽成 window.fbHomeTaskDrop(row, wasStatus) 放 assets/js/app.js,
  两个入口共用一份。作用域限定 [data-tk-home-host]:任务**看板**页的行也带
  data-task-row,那里勾完成只改状态、**必须保留该行**(看板按状态分栏,删了就没处看)。
- task_panel.js 的 syncRow() 在 d.status === 'done' 时调它;
  wasStatus 必须在改写 data-tk-status **之前**读,否则概述卡不知道该减「在做」还是「待办」。
- home.php 原来那份本地实现改成薄转发,避免两份实现改一处忘一处。
- 顺带修掉一条说谎的注释:原注释写"补偿了一个可撤回的提示",但代码里从来没有
  撤回按钮,只有一句 3.2 秒自动消失的 say()。
- _card_tasks.php 补记"两个入口"这件事。

子任务安全:task_panel_json() 用 panel_of 找**父任务**,回的 status 是父任务的,
所以面板里勾子任务不会把父任务那一行删掉(判据 ⑱ 专门守这条)。

判据:
- 新增 _b38_panel_done_drop.py(21 条,走真实浮层点击):
  修复前 **16 通过 / 5 失败**(⑥⑦⑧⑬⑭)→ 修复后 **21 / 0**
- 注入反证 _b38_falsify.py:把删行那两行用 if(false) 挡掉,锚点断言 count==1,
  立刻红回同样的 5 条、关键条目 ⑥⑧⑬⑭ 命中 4/4,副本自动还原
- 原 _b37_home_task_drop.py(复选框路径)仍 **20 / 0**
- _b37_regress.py **11 / 0**:看板仍改状态且**不删行**、详情页子任务复选框正常、
  空态 display 真的是 none
- home.php 内联 JS 0 块坏;app.js / task_panel.js node --check 全过
2026-10-28 18:29:59 +08:00

88 lines
5.7 KiB
PHP
Raw 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
/**
* 首页卡片 · 近期任务(v62 从 templates/home.php 拆出来的)。
*
* 用户原话(v24 第 24 批 第 3 条):「右侧显示任务模块里面的数据,显示当前正在
* 进行当中的所有的任务,除了显示任务的标题之外,还要显示他的状态。比如说正在
* 进行中待确认等等。除了这之外,还要显示他所处的看板的名称。」
*
* 🔴 v53 第 2 条:待办也放进来,放在进行中的**下面**(不是混排)。
* ⚠️ 每行前面有复选框,点了就地完成(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" data-tk-home-host>
<?php /* 🔴 工单 #20(用户 2026-10-28):「首页,近期任务,如果勾选了完成,
应该刷新一下这个列表,把完成的删掉。」
⇒ 勾完要把这一行**从列表里摘掉**(原来只改徽章文字,见 home.php 那段脚本)。
⚠️ 但"删到一条不剩"时卡里不能是一片空白 —— 下面这句 `empty-hint` 由服务端渲染,
前端删完不会自己冒出来,所以脚本要能在空的时候把它补上。
`data-tk-empty` 就是给脚本找它的钩子(不靠文案匹配,文案改一个字就失效)。
🔴🔴 把任务标成完成在首页有**两个入口**,两个都要删行:
① 每行前面那颗复选框 → 脚本在 `home.php` 末尾
② **点整行弹浮层** → 面板里的「状态」三段按钮 → `assets/js/task_panel.js`
真正干活的是 `assets/js/app.js` 的 `window.fbHomeTaskDrop()`,两个入口共用。
⚠️ 第一版只改了 ①,用户点的是 ②,于是"改完还是不消失"——
而我的判据只测了 ①,所以 20/0 全绿也是假绿。
**需求有几个入口,就要有几份判据。** */ ?>
<p class="empty-hint" data-tk-empty<?= $tasks ? ' hidden' : '' ?>>没有进行中的任务,也没有待办。</p>
<?php if ($tasks): ?>
<ul class="dash-list tk-list">
<?php foreach ($tasks as $t): ?>
<?php $late = \App\Task::isOverdue($t['due_at'] ?? null); ?>
<?php $tkColor = \App\Task::boardColorById((int)\App\Auth::familyId(),
(int)($t['board_id'] ?? 0) ?: null); ?>
<li class="dash-row tk-row" data-task-row="<?= (int)$t['id'] ?>"
style="--tk-c: <?= e($tkColor) ?>;">
<?php
$tkId = (int)$t['id']; $tkStatus = (string)($t['status'] ?? 'todo');
$tkBack = 'home'; $tkStatic = !\App\Auth::canWrite();
require __DIR__ . '/../_task_check.php';
?>
<span class="dash-dot" style="background: <?= e($late ? 'var(--red)' : 'var(--tk-c)') ?>;"></span>
<a class="dash-what" href="<?= e(url('tasks/show', ['id' => (int)$t['id']])) ?>"
title="<?= e((string)$t['title']) ?>"
data-fb-entry='<?= fb_entry_json([
'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) : ''],
['看板', trim((string)($t['board_name'] ?? ''))]]) ?>'
style="color: <?= e($late ? 'var(--red)' : $tkColor) ?>;">
<?= e((string)$t['title']) ?>
</a>
<span class="kb-st is-<?= e((string)($t['status'] ?? 'todo')) ?> is-static">
<?= e(\App\Task::statusWord((string)($t['status'] ?? 'todo'))) ?>
</span>
<?php if (trim((string)($t['board_name'] ?? '')) !== ''): ?>
<?php $bColor = \App\Task::boardColor($t['board_color'] ?? null, (string)$t['board_name']); ?>
<span class="tk-board" style="--bc: <?= e($bColor) ?>;">
<i class="tk-board-dot" aria-hidden="true"></i><?= e((string)$t['board_name']) ?>
</span>
<?php endif; ?>
<?php if ((int)($t['child_total'] ?? 0) > 0): ?>
<span class="dash-tag"><?= (int)$t['child_done'] ?>/<?= (int)$t['child_total'] ?></span>
<?php endif; ?>
<?php if (($t['due_at'] ?? '') !== ''): ?>
<span class="tk-due"><?= e(\App\Task::dueWord($t['due_at'] ?? null)) ?></span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>