diff --git a/client/src/pdd_ui.py b/client/src/pdd_ui.py index 5d41b85..caff2c9 100644 --- a/client/src/pdd_ui.py +++ b/client/src/pdd_ui.py @@ -503,11 +503,11 @@ class PDDTaskPage(QWidget): self.invertSelectionButton.setEnabled(False) self.rerunButton = PushButton(FIF.UPDATE, "重新采集", self) - self.rerunButton.setAccessibleName("重新采集勾选的采集任务") + self.rerunButton.setAccessibleName("重新采集勾选任务或当前任务") self.rerunButton.setEnabled(False) self.purchaseRerunButton = PushButton(FIF.SHOPPING_CART, "重新采购", self) - self.purchaseRerunButton.setAccessibleName("重新采购勾选的采购任务") + self.purchaseRerunButton.setAccessibleName("重新采购勾选任务或当前任务") self.purchaseRerunButton.setEnabled(False) self.resubmitButton = PushButton(FIF.SEND, "重新上报", self) @@ -802,16 +802,35 @@ class PDDTaskPage(QWidget): return if self._rerun_state != "idle": return - task_ids = self.taskModel.checked_task_ids() - if task_ids: - self.rerunRequested.emit(task_ids) + task_ids = self._rerun_target_task_ids() + if not task_ids: + self._show_missing_rerun_target() + return + self.rerunRequested.emit(task_ids) def _request_purchase_rerun(self) -> None: if self._rerun_state != "idle": return - task_ids = self.taskModel.checked_task_ids() - if task_ids: - self.purchaseRerunRequested.emit(task_ids) + task_ids = self._rerun_target_task_ids() + if not task_ids: + self._show_missing_rerun_target() + return + self.purchaseRerunRequested.emit(task_ids) + + def _rerun_target_task_ids(self) -> tuple[str, ...]: + """返回重新执行目标;勾选集优先,没有勾选时使用当前行。""" + + checked_task_ids = self.taskModel.checked_task_ids() + if checked_task_ids: + return checked_task_ids + + current_task_id = self.current_task_id() + return (current_task_id,) if current_task_id else () + + def _show_missing_rerun_target(self) -> None: + """在没有勾选和当前行时给出可操作的中文提示。""" + + self.statusLabel.setText("请勾选任务或单击选择一行") def _request_resubmit(self) -> None: if self._resubmit_running or self._remove_running: @@ -852,19 +871,15 @@ class PDDTaskPage(QWidget): return self.rerunButton.setText("重新采集") - self.rerunButton.setAccessibleName("重新采集勾选的采集任务") + self.rerunButton.setAccessibleName("重新采集勾选任务或当前任务") self.purchaseRerunButton.setText("重新采购") idle = not self._resubmit_running and not self._remove_running has_tasks = self.taskModel.data_row_count() > 0 self.autoFetchButton.setEnabled(idle) self.selectAllButton.setEnabled(idle and has_tasks) self.invertSelectionButton.setEnabled(idle and has_tasks) - self.rerunButton.setEnabled( - idle and bool(self.taskModel.checked_task_ids()) - ) - self.purchaseRerunButton.setEnabled( - idle and bool(self.taskModel.checked_task_ids()) - ) + self.rerunButton.setEnabled(idle and has_tasks) + self.purchaseRerunButton.setEnabled(idle and has_tasks) self.resubmitButton.setEnabled( idle and bool(self.taskModel.checked_task_ids()) ) diff --git a/client/test/test_pdd_ui.py b/client/test/test_pdd_ui.py index 79f34fd..732ad1e 100644 --- a/client/test/test_pdd_ui.py +++ b/client/test/test_pdd_ui.py @@ -232,6 +232,69 @@ class TaskTableModelCheckTests(unittest.TestCase): self.assertEqual(received, [("TASK-1", "TASK-2")]) page.deleteLater() + def test_rerun_uses_current_row_when_no_task_is_checked(self): + page = PDDTaskPage() + page.set_tasks( + [TaskRow("COL-1", "采集"), TaskRow("PUR-1", "采购")] + ) + collect_requests = [] + purchase_requests = [] + page.rerunRequested.connect(collect_requests.append) + page.purchaseRerunRequested.connect(purchase_requests.append) + + page.taskTable.setCurrentIndex(page.taskModel.index(0, TITLE_COLUMN)) + page.rerunButton.click() + page.taskTable.setCurrentIndex(page.taskModel.index(1, TITLE_COLUMN)) + page.purchaseRerunButton.click() + + self.assertEqual(collect_requests, [("COL-1",)]) + self.assertEqual(purchase_requests, [("PUR-1",)]) + self.assertEqual(page.taskModel.checked_task_ids(), ()) + page.deleteLater() + + def test_rerun_checked_tasks_take_priority_over_current_row(self): + page = PDDTaskPage() + page.set_tasks( + [TaskRow("COL-1", "采集"), TaskRow("COL-2", "采集")] + ) + received = [] + page.rerunRequested.connect(received.append) + page.taskTable.setCurrentIndex(page.taskModel.index(0, TITLE_COLUMN)) + page.taskModel.setData( + page.taskModel.index(1, CHECK_COLUMN), + Qt.Checked, + Qt.CheckStateRole, + ) + + page.rerunButton.click() + + self.assertEqual(received, [("COL-2",)]) + page.deleteLater() + + def test_rerun_without_checked_or_current_task_shows_guidance(self): + page = PDDTaskPage() + page.set_tasks([TaskRow("COL-1", "采集")]) + page.taskTable.clearSelection() + page.taskTable.setCurrentIndex(page.taskModel.index(-1, -1)) + + page.rerunButton.click() + + self.assertEqual( + page.statusLabel.text(), "请勾选任务或单击选择一行" + ) + page.deleteLater() + + def test_resubmit_and_remove_still_require_checked_tasks(self): + page = PDDTaskPage() + page.set_tasks([TaskRow("TASK-1", "采集")]) + page.taskTable.setCurrentIndex(page.taskModel.index(0, TITLE_COLUMN)) + + self.assertTrue(page.rerunButton.isEnabled()) + self.assertTrue(page.purchaseRerunButton.isEnabled()) + self.assertFalse(page.resubmitButton.isEnabled()) + self.assertFalse(page.removeButton.isEnabled()) + page.deleteLater() + def test_auto_fetch_text_and_keyword_width(self): page = PDDTaskPage() diff --git a/client/test/test_pdd_ui_event.py b/client/test/test_pdd_ui_event.py index 649370b..fa40643 100644 --- a/client/test/test_pdd_ui_event.py +++ b/client/test/test_pdd_ui_event.py @@ -385,8 +385,8 @@ class PDDTaskPageEventTest(unittest.TestCase): events = PDDTaskPageEvent(page, self.repository) events.load_initial_tasks() - self.assertFalse(page.rerunButton.isEnabled()) - self.assertFalse(page.purchaseRerunButton.isEnabled()) + self.assertTrue(page.rerunButton.isEnabled()) + self.assertTrue(page.purchaseRerunButton.isEnabled()) page.taskModel.setData( page.taskModel.index(0, 0), Qt.Checked, Qt.CheckStateRole ) diff --git a/docs/client/05-ui-specification.md b/docs/client/05-ui-specification.md index 5dfbd22..1bef8f8 100644 --- a/docs/client/05-ui-specification.md +++ b/docs/client/05-ui-specification.md @@ -95,7 +95,7 @@ Client 首次显示主窗口时使用标准 Windows 最大化状态,不使用 - 页面右侧显示“全选”“反选”和“已选 N 条”,之后依次是“重新上报”“重新采集”“重新采购”“删除”。 - “全选”和“反选”只作用于当前筛选条件下已经加载的真实任务行,空白占位行不参与。全选后新领取或继续增量加载的任务默认不勾选,防止批量操作范围在用户不知情时扩大。 - 没有真实任务,或正在重新执行、重新上报、删除时,禁用“全选”和“反选”。 -- 勾选一条或多条任务后可以点击“重新采集”或“重新采购”。点击后先批量预检,再显示可执行、类型过滤和安全阻止数量;“取消”默认聚焦,点击、按 `Escape` 或关闭弹窗都不得启动任务。 +- “重新采集”和“重新采购”优先作用于复选框勾选集;没有勾选时,作用于鼠标单击或键盘选中的当前行。两者都没有时提示“请勾选任务或单击选择一行”。点击后先批量预检,再显示可执行、类型过滤和安全阻止数量;“取消”默认聚焦,点击、按 `Escape` 或关闭弹窗都不得启动任务。 - 用户确认后,工作线程必须先检查已保存 Android 设备的实际连接状态,再重置任务和创建执行记录。检查失败时保留原任务状态、采集结果和执行历史。 - 重新采集只处理采集任务,重新采购只处理采购任务,另一类型计入过滤数量。执行中、结果待提交、仍有未发送 Outbox、已经排队或自动获取忙碌时必须阻止,并用中文说明原因。 - 确认后只执行选中的稳定任务编号,不领取新任务,不先处理其他任务或 Outbox。 @@ -147,6 +147,7 @@ Client 首次显示主窗口时使用标准 Windows 最大化状态,不使用 - 默认排序为 `updated_at DESC, id DESC`。 - 排序、筛选和数据变化后以稳定任务编号恢复当前行。 - 当前行和勾选集相互独立;点击复选框不打开详情,也不启动任何业务操作。 +- 当前行只作为“重新采集”和“重新采购”在勾选集为空时的单条兜底;已有勾选时不得把当前行混入操作范围。“重新上报”和“删除”仍只作用于勾选集。 - 勾选集使用 `remote_task_id` 保存。普通刷新保留仍存在的勾选,搜索条件变化清空勾选,避免操作被筛选隐藏的旧任务。 - 初始加载有限批次,滚动时通过 `canFetchMore/fetchMore` 增量加载后续任务。 - 单击非交互区域只设置当前行和选择。