From 57fbdbf25a5d2997ae707471cbd627f6fbc67326 Mon Sep 17 00:00:00 2001 From: chengkai3 Date: Sat, 20 Jun 2026 15:10:03 +0800 Subject: [PATCH] =?UTF-8?q?[fix]:[FL-156][=E4=BB=BB=E5=8A=A1=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E7=A7=BB=E5=8A=A8=E7=AB=AF=E6=97=A0=E9=99=90=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E5=AF=B9=E9=BD=90]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- memory/2026-06-20.md | 22 ++++ web/src/app/admin/task-monitor/page.tsx | 134 ++++++++++++++++++++---- 2 files changed, 133 insertions(+), 23 deletions(-) diff --git a/memory/2026-06-20.md b/memory/2026-06-20.md index 2168495..d5b0dee 100644 --- a/memory/2026-06-20.md +++ b/memory/2026-06-20.md @@ -447,3 +447,25 @@ - 风险与关注点: - 改动仅影响角色管理页前端表格分页回调和 hook 组织顺序,不改变接口、schema、权限或 CRUD 语义。 + +## Follow-up - 任务监控页移动端无限滚动对齐(FL-156) + +- 背景: + - 复核指出任务监控页移动端卡片一次性渲染全部筛选任务,缺少与用户管理页一致的卡片分页累积、滚动加载和加载更多状态。 + +- 本次处理: + - 任务监控页补齐 `cardViewPage`、`allLoadedTasks`、`isLoadingMore` 和 `pageCardRef`,与用户管理页移动卡片基础设施对齐。 + - 移动端卡片视图改为按当前 pageSize 分页累积渲染,滚动到卡片容器底部时自动追加下一页。 + - 卡片视图补齐“加载更多...”指示器,并仅在已加载全部筛选任务时显示“已加载全部 X 条数据”。 + - 筛选条件变更和重置筛选时同步重置表格分页、卡片页码、累积任务和加载更多状态。 + - 移动端筛选 `Form.Item` 的 `marginBottom` 统一调整为 0,对齐用户管理页表单样式口径。 + +- 验证: + - 基线:`npm --workspace web exec eslint src/app/admin/task-monitor/page.tsx --max-warnings=0` 通过。 + - 基线:`npm --workspace web exec tsc --noEmit` 通过。 + - 修改后:`npm --workspace web exec eslint src/app/admin/task-monitor/page.tsx --max-warnings=0` 通过。 + - 修改后:`npm --workspace web exec tsc --noEmit` 通过。 + - 修改后:`node --experimental-strip-types web/src/lib/task-monitor-display.test.js` 通过,4 passed,仍有既有 MODULE_TYPELESS_PACKAGE_JSON warning。 + +- 风险与关注点: + - 任务监控数据仍来自既有 Flower 批量接口,本次只改变移动端前端渲染批次和滚动加载交互,不改变接口字段、权限或任务筛选规则。 diff --git a/web/src/app/admin/task-monitor/page.tsx b/web/src/app/admin/task-monitor/page.tsx index 5ae17a8..215f56b 100644 --- a/web/src/app/admin/task-monitor/page.tsx +++ b/web/src/app/admin/task-monitor/page.tsx @@ -164,6 +164,18 @@ export default function AdminTaskMonitorPage() { const [tableScrollY, setTableScrollY] = useState(TASK_MONITOR_TABLE_MIN_SCROLL_Y); const tableScrollAnchorRef = useRef(null); const viewMode: "table" | "card" = isMobile ? "card" : "table"; + const [cardViewPage, setCardViewPage] = useState(1); + const [allLoadedTasks, setAllLoadedTasks] = useState([]); + const [isLoadingMore, setIsLoadingMore] = useState(false); + const pageCardRef = useRef(null); + const { current: paginationCurrent, pageSize: paginationPageSize } = pagination; + + const resetTaskListPagination = useCallback(() => { + setPagination((prev) => ({ ...prev, current: 1 })); + setCardViewPage(1); + setAllLoadedTasks([]); + setIsLoadingMore(false); + }, []); const workersOverviewQuery = useQuery({ queryKey: ["flower-workers-overview"], @@ -381,7 +393,7 @@ export default function AdminTaskMonitorPage() { setQueueKeyword(""); setTaskKeyword(""); setStatusFilter("all"); - setPagination((prev) => ({ ...prev, current: 1 })); + resetTaskListPagination(); }; const workersOverviewErrorMessage = workersOverviewQuery.error instanceof Error @@ -396,6 +408,74 @@ export default function AdminTaskMonitorPage() { errorMessage: anyError, }); + useEffect(() => { + if (viewMode !== "card" || allTasksQuery.isLoading) { + return; + } + + const frameId = window.requestAnimationFrame(() => { + const nextTasks = filteredTaskRows.slice(0, cardViewPage * paginationPageSize); + + if (cardViewPage === 1) { + setAllLoadedTasks(() => nextTasks); + } else { + setAllLoadedTasks((prev) => { + if (nextTasks.length === 0) { + return prev; + } + const existingKeys = new Set(prev.map((task) => task.key)); + const newTasks = nextTasks.filter((task) => !existingKeys.has(task.key)); + return [...prev, ...newTasks]; + }); + } + + setIsLoadingMore(false); + }); + + return () => { + window.cancelAnimationFrame(frameId); + }; + }, [allTasksQuery.isLoading, cardViewPage, filteredTaskRows, paginationPageSize, viewMode]); + + useEffect(() => { + if (viewMode !== "card") { + return; + } + + const pageCard = pageCardRef.current; + if (!pageCard) { + return; + } + + const cardBody = pageCard.querySelector(".ant-card-body"); + if (!cardBody) { + return; + } + + const handleScroll = () => { + if (isLoadingMore || allTasksQuery.isLoading) { + return; + } + + const { scrollTop, scrollHeight, clientHeight } = cardBody; + if (scrollTop + clientHeight >= scrollHeight - 100) { + const total = filteredTaskRows.length; + const loadedCount = allLoadedTasks.length; + + if (loadedCount < total) { + setIsLoadingMore(true); + setCardViewPage((prev) => prev + 1); + setPagination((prev) => ({ ...prev, current: prev.current + 1 })); + } + } + }; + + cardBody.addEventListener("scroll", handleScroll); + return () => { + cardBody.removeEventListener("scroll", handleScroll); + }; + }, [allLoadedTasks.length, allTasksQuery.isLoading, filteredTaskRows.length, isLoadingMore, viewMode]); + const updateTableScrollY = useCallback(() => { if (typeof window === "undefined") { return; @@ -578,6 +658,7 @@ export default function AdminTaskMonitorPage() { return (
{viewMode === "card" ? (
- + { setWorkerKeyword(event.target.value); - setPagination((prev) => ({ ...prev, current: 1 })); + resetTaskListPagination(); }} /> - + { setQueueKeyword(event.target.value); - setPagination((prev) => ({ ...prev, current: 1 })); + resetTaskListPagination(); }} /> - + { setTaskKeyword(event.target.value); - setPagination((prev) => ({ ...prev, current: 1 })); + resetTaskListPagination(); }} /> - +