[fix]:[FL-160][对齐系统日志筛选交互]
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -327,3 +327,27 @@
|
||||
|
||||
- 风险与关注点:
|
||||
- 改动涉及 `GET /api/v1/tower-models` 列表分页契约,未改变响应字段、CRUD 字段、权限码或图片上传/预览接口。
|
||||
|
||||
## Follow-up - 系统日志筛选交互细节对齐(FL-160)
|
||||
|
||||
- 背景:
|
||||
- 评审继续指出系统日志页仍保留“查询/重置筛选”按钮,和用户管理页的 500ms 防抖自动搜索模式不一致。
|
||||
|
||||
- 本次处理:
|
||||
- 移除系统日志页桌面端和移动端的“查询”“重置筛选”按钮,筛选完全依赖输入框 500ms 防抖自动触发。
|
||||
- 移动端保留多个筛选字段的 label,但移除按钮组,并将末尾表单项 `marginBottom` 对齐为 0。
|
||||
- 表格 loading 从 `logsQuery.isFetching` 改为 `logsQuery.isLoading`,对齐用户管理页初次加载态口径。
|
||||
- 表格分页补齐 `showSizeChanger: true` 与 `[10, 20, 50, 100]`,并让请求 `limit` 跟随分页 pageSize。
|
||||
- 表格和移动卡片空态文案统一为“未找到符合筛选条件的日志记录。”。
|
||||
|
||||
- 验证:
|
||||
- 基线:`npm --workspace web exec eslint src/app/admin/users/page.tsx src/app/admin/syslog/page.tsx` 通过,仅用户页存在 1 条既有 unused eslint-disable warning。
|
||||
- 基线:`npm --workspace web exec tsc --noEmit --pretty false` 失败,失败点均在既有 `src/app/admin/elevation-records/page.tsx`,与系统日志页无关。
|
||||
- 修改后:`npm --workspace web exec eslint src/app/admin/syslog/page.tsx --max-warnings=0` 通过。
|
||||
- 修改后:`npm --workspace web exec eslint src/app/admin/users/page.tsx src/app/admin/syslog/page.tsx` 通过,仍仅用户页 1 条既有 warning。
|
||||
- 修改后:`npm --workspace web exec tsc --noEmit --pretty false` 仍失败于既有 `src/app/admin/elevation-records/page.tsx`。
|
||||
- 修改后:`npm run build:web` 编译通过后在 TypeScript 阶段失败于同一既有 `src/app/admin/elevation-records/page.tsx:91`。
|
||||
|
||||
- 风险与关注点:
|
||||
- 改动仅影响 `/admin/syslog` 前端筛选交互、分页展示和空态文案,不改变 `/api/v1/admin/audit-logs` 字段或权限语义。
|
||||
- 当前 dev 分支存在 unrelated `elevation-records` TypeScript 错误,会阻断全量 `tsc` 与 `next build`。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Button, Card, Col, Empty, Form, Input, Row, Space, Spin, Table, Tag, Typography, type CardProps } from "antd";
|
||||
import { Card, Col, Empty, Form, Input, Row, Space, Spin, Table, Tag, Typography, type CardProps } from "antd";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import type { CSSProperties, ComponentType, RefAttributes } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
@@ -44,6 +44,7 @@ export default function AdminSyslogPage() {
|
||||
const isMobile = useMobileDetection();
|
||||
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(PAGE_SIZE);
|
||||
const [draftFilters, setDraftFilters] = useState<Filters>(EMPTY_FILTERS);
|
||||
const [filters, setFilters] = useState<Filters>(EMPTY_FILTERS);
|
||||
const [error, setError] = useState("");
|
||||
@@ -103,7 +104,7 @@ export default function AdminSyslogPage() {
|
||||
|
||||
const logsPath = useMemo(() => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("limit", String(PAGE_SIZE));
|
||||
params.set("limit", String(pageSize));
|
||||
params.set("offset", String(offset));
|
||||
if (filters.action.trim()) {
|
||||
params.set("action", filters.action.trim());
|
||||
@@ -112,7 +113,7 @@ export default function AdminSyslogPage() {
|
||||
params.set("user_id", filters.user_id.trim());
|
||||
}
|
||||
return `/api/v1/admin/audit-logs?${params.toString()}`;
|
||||
}, [filters.action, filters.user_id, offset]);
|
||||
}, [filters.action, filters.user_id, offset, pageSize]);
|
||||
|
||||
const loadLogs = useCallback(async () => {
|
||||
const response = await fetchWithAuth(logsPath);
|
||||
@@ -139,7 +140,7 @@ export default function AdminSyslogPage() {
|
||||
const total = logsQuery.data?.total ?? 0;
|
||||
const queryError = logsQuery.error instanceof Error ? logsQuery.error.message : "";
|
||||
const anyError = error || queryError;
|
||||
const currentPage = Math.floor(offset / PAGE_SIZE) + 1;
|
||||
const currentPage = Math.floor(offset / pageSize) + 1;
|
||||
|
||||
useToastFeedback({
|
||||
errorMessage: anyError,
|
||||
@@ -198,14 +199,14 @@ export default function AdminSyslogPage() {
|
||||
if (loadedCount < total) {
|
||||
setIsLoadingMore(true);
|
||||
setCardViewPage((prev) => prev + 1);
|
||||
setOffset((prev) => prev + PAGE_SIZE);
|
||||
setOffset((prev) => prev + pageSize);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cardBody.addEventListener("scroll", handleScroll);
|
||||
return () => cardBody.removeEventListener("scroll", handleScroll);
|
||||
}, [viewMode, isLoadingMore, logsQuery.isLoading, total, allLoadedLogs.length]);
|
||||
}, [viewMode, isLoadingMore, logsQuery.isLoading, total, allLoadedLogs.length, pageSize]);
|
||||
|
||||
// Reset card view state when filters change
|
||||
useEffect(() => {
|
||||
@@ -414,7 +415,7 @@ export default function AdminSyslogPage() {
|
||||
>
|
||||
{viewMode === "card" ? (
|
||||
<Form layout="vertical" style={{ marginBottom: 16 }}>
|
||||
<Form.Item label="动作">
|
||||
<Form.Item label="动作" style={{ marginBottom: 12 }}>
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="按动作筛选(如 auth.login)"
|
||||
@@ -422,7 +423,7 @@ export default function AdminSyslogPage() {
|
||||
onChange={(event) => handleActionChange(event.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="用户ID">
|
||||
<Form.Item label="用户ID" style={{ marginBottom: 0 }}>
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="按用户ID筛选(如 openclaw)"
|
||||
@@ -430,31 +431,6 @@ export default function AdminSyslogPage() {
|
||||
onChange={(event) => handleUserIdChange(event.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space size={8}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setOffset(0);
|
||||
setFilters({
|
||||
action: draftFilters.action.trim(),
|
||||
user_id: draftFilters.user_id.trim(),
|
||||
});
|
||||
}}
|
||||
>
|
||||
查询
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOffset(0);
|
||||
setDraftFilters(EMPTY_FILTERS);
|
||||
setFilters(EMPTY_FILTERS);
|
||||
}}
|
||||
>
|
||||
重置筛选
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
) : (
|
||||
<Form layout="inline" style={{ rowGap: 12 }}>
|
||||
@@ -474,31 +450,6 @@ export default function AdminSyslogPage() {
|
||||
onChange={(event) => handleUserIdChange(event.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space size={8}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setOffset(0);
|
||||
setFilters({
|
||||
action: draftFilters.action.trim(),
|
||||
user_id: draftFilters.user_id.trim(),
|
||||
});
|
||||
}}
|
||||
>
|
||||
查询
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOffset(0);
|
||||
setDraftFilters(EMPTY_FILTERS);
|
||||
setFilters(EMPTY_FILTERS);
|
||||
}}
|
||||
>
|
||||
重置筛选
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)}
|
||||
|
||||
@@ -512,21 +463,24 @@ export default function AdminSyslogPage() {
|
||||
rowKey={(record) => String(record.id)}
|
||||
columns={columns}
|
||||
dataSource={logs}
|
||||
loading={logsQuery.isFetching}
|
||||
loading={logsQuery.isLoading}
|
||||
tableLayout="fixed"
|
||||
pagination={{
|
||||
current: currentPage,
|
||||
pageSize: PAGE_SIZE,
|
||||
pageSize,
|
||||
total: Math.max(total, 1),
|
||||
onChange: (page) => setOffset((page - 1) * PAGE_SIZE),
|
||||
showSizeChanger: false,
|
||||
showQuickJumper: false,
|
||||
onChange: (page, nextPageSize) => {
|
||||
setPageSize(nextPageSize);
|
||||
setOffset((page - 1) * nextPageSize);
|
||||
},
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
showTotal: () => `共 ${total} 条`,
|
||||
hideOnSinglePage: false,
|
||||
style: { marginBottom: 0 },
|
||||
}}
|
||||
locale={{
|
||||
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无日志数据" />,
|
||||
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="未找到符合筛选条件的日志记录。" />,
|
||||
}}
|
||||
scroll={{ y: tableScrollY }}
|
||||
/>
|
||||
@@ -541,7 +495,7 @@ export default function AdminSyslogPage() {
|
||||
<div className="admin-syslog-card-view-state">
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="暂无日志数据"
|
||||
description="未找到符合筛选条件的日志记录。"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user