From 214605d912f6ed0a5abf6d3dca8c9e38c5b54f00 Mon Sep 17 00:00:00 2001 From: ayou <550244300@qq.com> Date: Thu, 25 Sep 2025 23:52:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=B9=B6=E6=B7=BB=E5=8A=A0=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为多个页面组件添加统一的分页统计显示和批量删除功能 在日志管理页面添加批量删除接口和前端实现 优化表格分页配置,统一显示总条目数和分页选项 --- backend/src/routes/flow_run_logs.rs | 50 ++++++++++++++--- backend/src/routes/logs.rs | 48 +++++++++++++++-- backend/src/services/flow_run_log_service.rs | 11 +++- backend/src/services/log_service.rs | 10 ++++ frontend/src/pages/Departments.tsx | 43 ++++++++++++--- frontend/src/pages/FlowList.tsx | 10 +++- frontend/src/pages/FlowRunLogs.tsx | 43 +++++++++++++-- frontend/src/pages/Logs.tsx | 57 ++++++++++++++++---- frontend/src/pages/Menus.tsx | 38 ++++++++++--- frontend/src/pages/Permissions.tsx | 2 +- frontend/src/pages/Positions.tsx | 11 +++- frontend/src/pages/Roles.tsx | 11 +++- frontend/src/pages/ScheduleJobs.tsx | 5 +- frontend/src/pages/Users.tsx | 12 ++++- 14 files changed, 303 insertions(+), 48 deletions(-) diff --git a/backend/src/routes/flow_run_logs.rs b/backend/src/routes/flow_run_logs.rs index 87ea764..a0b6ba9 100644 --- a/backend/src/routes/flow_run_logs.rs +++ b/backend/src/routes/flow_run_logs.rs @@ -1,13 +1,47 @@ -use axum::{Router, routing::get, extract::{State, Query}, Json}; -use crate::{db::Db, response::ApiResponse, services::flow_run_log_service}; +//! 流程运行日志路由模块 +//! +//! 提供流程运行日志的分页查询接口与批量删除接口。 +use axum::{ + extract::{Query, State}, + routing::{get, delete}, + Json, Router, +}; +use axum::extract::Path; + +use crate::db::Db; +use crate::error::AppError; +use crate::response::ApiResponse; +use crate::services::flow_run_log_service; + +/// 路由定义:流程运行日志相关接口 pub fn router() -> Router { - Router::new().route("/flow_run_logs", get(list)) + Router::new() + .route("/flow_run_logs", get(list)) + .route("/flow_run_logs/{ids}", delete(delete_many)) } -async fn list(State(db): State, Query(p): Query) -> Json>> { - match flow_run_log_service::list(&db, p).await { - Ok(res) => Json(ApiResponse::ok(res)), - Err(e) => Json(ApiResponse::err(500, format!("{}", e))), - } +/// 流程运行日志列表 +async fn list( + State(db): State, + Query(p): Query, +) -> Result>>, AppError> { + let res = flow_run_log_service::list(&db, p).await?; + Ok(Json(ApiResponse::ok(res))) +} + +/// 批量删除流程运行日志 +/// +/// 路径参数 ids 采用逗号分隔的 ID 列表,例如:/flow_run_logs/1001,1002 +/// 返回:{ deleted: },deleted 表示实际删除条数 +async fn delete_many( + State(db): State, + Path(ids): Path, +) -> Result>, AppError> { + let id_list: Vec = ids + .split(',') + .filter_map(|s| s.trim().parse::().ok()) + .collect(); + let deleted = flow_run_log_service::delete_many(&db, id_list).await?; + Ok(Json(ApiResponse::ok(serde_json::json!({ "deleted": deleted })))) } \ No newline at end of file diff --git a/backend/src/routes/logs.rs b/backend/src/routes/logs.rs index 5a63ba7..415a560 100644 --- a/backend/src/routes/logs.rs +++ b/backend/src/routes/logs.rs @@ -1,9 +1,47 @@ -use axum::{Router, routing::get, extract::{Query, State}, Json}; -use crate::{db::Db, response::ApiResponse, services::log_service, error::AppError}; +//! 请求日志查询路由模块 +//! +//! 提供系统请求日志的分页查询接口与批量删除接口。 -pub fn router() -> Router { Router::new().route("/logs", get(list)) } +use axum::{ + extract::{Query, State}, + routing::{get, delete}, + Json, Router, +}; +use axum::extract::Path; -async fn list(State(db): State, Query(p): Query) -> Result>>, AppError> { - let res = log_service::list(&db, p).await.map_err(|e| AppError::Anyhow(anyhow::anyhow!(e)))?; +use crate::db::Db; +use crate::error::AppError; +use crate::response::ApiResponse; +use crate::services::log_service; + +/// 路由定义:日志相关接口 +pub fn router() -> Router { + Router::new() + .route("/logs", get(list)) + .route("/logs/{ids}", delete(delete_many)) +} + +/// 日志列表 +async fn list( + State(db): State, + Query(p): Query, +) -> Result>>, AppError> { + let res = log_service::list(&db, p).await?; Ok(Json(ApiResponse::ok(res))) +} + +/// 批量删除系统请求日志 +/// +/// 路径参数 ids 采用逗号分隔的 ID 列表,例如:/logs/1,2,3 +/// 返回:{ deleted: },deleted 表示实际删除条数 +async fn delete_many( + State(db): State, + Path(ids): Path, +) -> Result>, AppError> { + let id_list: Vec = ids + .split(',') + .filter_map(|s| s.trim().parse::().ok()) + .collect(); + let deleted = log_service::delete_many(&db, id_list).await?; + Ok(Json(ApiResponse::ok(serde_json::json!({ "deleted": deleted })))) } \ No newline at end of file diff --git a/backend/src/services/flow_run_log_service.rs b/backend/src/services/flow_run_log_service.rs index 8e75116..348bac0 100644 --- a/backend/src/services/flow_run_log_service.rs +++ b/backend/src/services/flow_run_log_service.rs @@ -62,7 +62,6 @@ pub async fn create(db: &Db, input: CreateRunLogInput) -> anyhow::Result { Ok(m.id) } - /// 分页查询流程运行日志 /// /// # 参数 @@ -129,4 +128,14 @@ pub async fn list(db: &Db, p: ListParams) -> anyhow::Result page, page_size, }) +} + +// 新增:批量删除流程运行日志 +pub async fn delete_many(db: &Db, ids: Vec) -> anyhow::Result { + if ids.is_empty() { return Ok(0); } + let res = flow_run_log::Entity::delete_many() + .filter(flow_run_log::Column::Id.is_in(ids)) + .exec(db) + .await?; + Ok(res.rows_affected as u64) } \ No newline at end of file diff --git a/backend/src/services/log_service.rs b/backend/src/services/log_service.rs index ebbaea7..58fcb72 100644 --- a/backend/src/services/log_service.rs +++ b/backend/src/services/log_service.rs @@ -68,4 +68,14 @@ pub async fn list(db: &Db, p: ListParams) -> anyhow::Result> { let total = paginator.num_items().await? as u64; let models = paginator.fetch_page(if page>0 { page-1 } else { 0 }).await?; Ok(PageResp { items: models.into_iter().map(Into::into).collect(), total, page, page_size }) +} + +// 新增:批量删除系统请求日志 +pub async fn delete_many(db: &Db, ids: Vec) -> anyhow::Result { + if ids.is_empty() { return Ok(0); } + let res = request_log::Entity::delete_many() + .filter(request_log::Column::Id.is_in(ids)) + .exec(db) + .await?; + Ok(res.rows_affected as u64) } \ No newline at end of file diff --git a/frontend/src/pages/Departments.tsx b/frontend/src/pages/Departments.tsx index 16d9e70..9d737fa 100644 --- a/frontend/src/pages/Departments.tsx +++ b/frontend/src/pages/Departments.tsx @@ -12,6 +12,9 @@ export default function Departments(){ const [loading, setLoading] = useState(false) const [data, setData] = useState([] as DeptItem[]) const [keyword, setKeyword] = useState('') + const [total, setTotal] = useState(0) + const [page, setPage] = useState(1) + const [pageSize, setPageSize] = useState(10) const [createOpen, setCreateOpen] = useState(false) const [editOpen, setEditOpen] = useState(false) @@ -20,18 +23,31 @@ export default function Departments(){ const [editForm] = Form.useForm() const [searchForm] = Form.useForm() - const fetchList = async (kw: string = keyword) => { + const fetchList = async (kw: string = keyword, p: number = page, ps: number = pageSize) => { setLoading(true) try{ - const { data } = await api.get('/departments', { params: { keyword: kw } }) - if(data?.code === 0){ setData(data.data || []) } else { throw new Error(data?.message || '获取部门失败') } + const { data } = await api.get('/departments', { params: { keyword: kw, page: p, page_size: ps } }) + if(data?.code === 0){ + const payload = data.data + if (payload && Array.isArray(payload.items)) { + const items = payload.items as DeptItem[] + setData(items) + setTotal(Number(payload.total ?? items.length ?? 0)) + setPage(Number(payload.page ?? p)) + setPageSize(Number(payload.page_size ?? ps)) + } else { + const list = (payload || []) as DeptItem[] + setData(list) + setTotal(Array.isArray(list) ? list.length : 0) + } + } else { throw new Error(data?.message || '获取部门失败') } }catch(e: any){ message.error(e.message || '获取部门失败') } finally { setLoading(false) } } const didInitFetchRef = useRef(false) useEffect(()=>{ if(didInitFetchRef.current) return didInitFetchRef.current = true - fetchList('') + fetchList('', 1, pageSize) }, []) // 构建部门树用于树形选择 @@ -182,25 +198,36 @@ export default function Departments(){ return (
-
{ const kw = String(vals?.keyword ?? '').trim(); setKeyword(kw); fetchList(kw) }} style={{ marginBottom: 12 }}> + { const kw = String(vals?.keyword ?? '').trim(); setKeyword(kw); fetchList(kw, 1, pageSize) }} style={{ marginBottom: 12 }}> - +
+ {/* 总条目数展示:在表格上方显示当前总数 */} +
总条目数:{total}
rowKey="id" loading={loading} dataSource={treeDataForTable} columns={columns} expandable={{ expandedRowKeys, onExpandedRowsChange: (keys) => setExpandedRowKeys(keys as React.Key[]), indentSize: 18, rowExpandable: (record: DeptItem) => Array.isArray(record.children) && record.children.length > 0 }} - pagination={false} + // 展示分页统计:显示总条目数(与流程日志一致的受控分页) + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + pageSizeOptions: [10, 20, 50, 100], + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p, ps) => { setPage(p); if (ps) setPageSize(ps) } + }} /> setCreateOpen(false)} okText="创建" width={840}> @@ -216,7 +243,7 @@ export default function Departments(){ filterTreeNode={(input: string, node: any) => String(node?.title ?? '').toLowerCase().includes(String(input).toLowerCase())} /> - + diff --git a/frontend/src/pages/FlowList.tsx b/frontend/src/pages/FlowList.tsx index a24f8b6..ab4455d 100644 --- a/frontend/src/pages/FlowList.tsx +++ b/frontend/src/pages/FlowList.tsx @@ -249,7 +249,15 @@ export default function FlowList() { loading={loading} dataSource={list} columns={columns as any} - pagination={{ current: page, pageSize, total, showSizeChanger: true, onChange: (p: number, ps?: number) => fetchList(p, ps ?? pageSize, keyword) }} + // 展示分页统计:在分页栏显示总条目数 + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p: number, ps?: number) => fetchList(p, ps ?? pageSize, keyword) + }} /> {/* 编辑基础信息弹窗 */} diff --git a/frontend/src/pages/FlowRunLogs.tsx b/frontend/src/pages/FlowRunLogs.tsx index 272a816..59abc2a 100644 --- a/frontend/src/pages/FlowRunLogs.tsx +++ b/frontend/src/pages/FlowRunLogs.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react' -import { Button, Form, Input, Select, Space, Table, Tag, Descriptions, DatePicker, Drawer, Typography } from 'antd' +import { Button, Form, Input, Select, Space, Table, Tag, Descriptions, DatePicker, Drawer, Typography, Popconfirm, message } from 'antd' import type { ColumnsType } from 'antd/es/table' import api, { ApiResp } from '../utils/axios' import dayjs from 'dayjs' @@ -30,9 +30,10 @@ export default function FlowRunLogs() { const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(10) - const [detailOpen, setDetailOpen] = useState(false) const [detail, setDetail] = useState(null) + // 选中项用于批量删除 + const [selectedKeys, setSelectedKeys] = useState([]) const fetchData = async (p = page, ps = pageSize) => { const v = form.getFieldsValue() @@ -57,6 +58,23 @@ export default function FlowRunLogs() { const openDetail = (record: RunLogItem) => { setDetail(record); setDetailOpen(true) } const closeDetail = () => setDetailOpen(false) + // 批量删除 + const handleBatchDelete = async () => { + if (!selectedKeys.length) return + try { + const { data } = await api.delete>(`/flow_run_logs/${selectedKeys.join(',')}`) + if (data?.code === 0) { + message.success(`已删除 ${data?.data?.deleted || 0} 条`) + setSelectedKeys([]) + fetchData(page, pageSize) + } else { + throw new Error(data?.message || '删除失败') + } + } catch (e: any) { + message.error(e.message || '删除失败') + } + } + const columns: ColumnsType = useMemo(() => [ { title: 'ID', dataIndex: 'id', width: 90 }, { title: '时间', dataIndex: 'started_at', width: 180, render: (v: string) => dayjs(v).format('YYYY-MM-DD HH:mm:ss') }, @@ -135,13 +153,32 @@ export default function FlowRunLogs() { + {/* // 操作区:批量删除 */} +
+ + + + + + +
rowKey="id" loading={loading} dataSource={data} columns={columns} scroll={{ x: 1600 }} - pagination={{ current: page, pageSize, total, showSizeChanger: true, pageSizeOptions: [10, 20, 50, 100], onChange: (p, ps) => fetchData(p, ps) }} + rowSelection={{ selectedRowKeys: selectedKeys, onChange: (keys) => setSelectedKeys(keys) }} + // 展示分页统计:在分页栏显示总条目数 + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + pageSizeOptions: [10, 20, 50, 100], + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p, ps) => fetchData(p, ps) + }} /> diff --git a/frontend/src/pages/Logs.tsx b/frontend/src/pages/Logs.tsx index ffbd84a..9beb0de 100644 --- a/frontend/src/pages/Logs.tsx +++ b/frontend/src/pages/Logs.tsx @@ -30,9 +30,10 @@ export default function Logs() { const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(10) - const [detailOpen, setDetailOpen] = useState(false) const [detail, setDetail] = useState(null) + // 选中项用于批量删除 + const [selectedKeys, setSelectedKeys] = useState([]) const fetchData = async (p = page, ps = pageSize) => { const v = form.getFieldsValue() @@ -59,16 +60,33 @@ export default function Logs() { const openDetail = (record: LogInfo) => { setDetail(record); setDetailOpen(true) } const closeDetail = () => setDetailOpen(false) + // 批量删除 + const handleBatchDelete = async () => { + if (!selectedKeys.length) return + try { + const { data } = await api.delete>(`/logs/${selectedKeys.join(',')}`) + if (data?.code === 0) { + message.success(`已删除 ${data?.data?.deleted || 0} 条`) + setSelectedKeys([]) + fetchData(page, pageSize) + } else { + throw new Error(data?.message || '删除失败') + } + } catch (e: any) { + message.error(e.message || '删除失败') + } + } + const columns = useMemo(() => [ - { title: 'ID', dataIndex: 'id', width: 80 }, - { title: '时间', dataIndex: 'request_time', width: 180, render: (v: string) => dayjs(v).format('YYYY-MM-DD HH:mm:ss') }, + { title: 'ID', dataIndex: 'id', width: 100 }, + { title: '时间', dataIndex: 'request_time', width: 120, render: (v: string) => dayjs(v).format('YYYY-MM-DD HH:mm:ss') }, { title: '路径', dataIndex: 'path', width: 240 }, - { title: '方法', dataIndex: 'method', width: 90, render: (m: string) => {m} }, - { title: '用户', dataIndex: 'username', width: 140, render: (_: any, r: LogInfo) => r.username || (r.user_id ? `UID:${r.user_id}` : '-') }, - { title: '状态码', dataIndex: 'status_code', width: 100 }, - { title: '耗时(ms)', dataIndex: 'duration_ms', width: 100 }, - { title: '请求参数', dataIndex: 'request_params', width: 260, ellipsis: true }, - { title: '响应参数', dataIndex: 'response_params', width: 260, ellipsis: true }, + { title: '方法', dataIndex: 'method', width: 60, render: (m: string) => {m} }, + { title: '用户', dataIndex: 'username', width: 100, render: (_: any, r: LogInfo) => r.username || (r.user_id ? `UID:${r.user_id}` : '-') }, + { title: '状态码', dataIndex: 'status_code', width: 60 }, + { title: '耗时(ms)', dataIndex: 'duration_ms', width: 80 }, + { title: '请求参数', dataIndex: 'request_params', width: 280, ellipsis: true }, + { title: '响应参数', dataIndex: 'response_params', width: 280, ellipsis: true }, { title: '操作', key: 'action', fixed: 'right' as any, width: 120, render: (_: any, r: LogInfo) => ( openDetail(r)}> @@ -129,13 +147,32 @@ export default function Logs() {
+ {/* 操作区:批量删除 */} +
+ + + + + + +
fetchData(p, ps) }} + rowSelection={{ selectedRowKeys: selectedKeys, onChange: (keys) => setSelectedKeys(keys) }} + // 展示分页统计:在分页栏显示总条目数 + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + pageSizeOptions: [10, 20, 50, 100], + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p, ps) => fetchData(p, ps) + }} /> diff --git a/frontend/src/pages/Menus.tsx b/frontend/src/pages/Menus.tsx index 1852a8d..0a97d24 100644 --- a/frontend/src/pages/Menus.tsx +++ b/frontend/src/pages/Menus.tsx @@ -122,6 +122,8 @@ export default function Menus(){ const [data, setData] = useState([] as MenuItem[]) const [parents, setParents] = useState([] as MenuItem[]) const [total, setTotal] = useState(0) + const [page, setPage] = useState(1) + const [pageSize, setPageSize] = useState(10) const [keyword, setKeyword] = useState('') // 是否显示按钮(type=3) const [showButtons, setShowButtons] = useState(true) @@ -234,16 +236,31 @@ export default function Menus(){ { name: 'ReloadOutlined', node: }, ], []) - const fetchMenus = async (kw: string = keyword) => { + const fetchMenus = async (kw: string = keyword, p: number = page, ps: number = pageSize) => { setLoading(true) try{ - const { data } = await api.get(`/menus`, { params: { keyword: kw } }) - if(data?.code === 0){ setData(data.data || []); setParents((data.data || []).filter((m: MenuItem) => m.type !== 3)) } + const { data } = await api.get(`/menus`, { params: { keyword: kw, page: p, page_size: ps } }) + if(data?.code === 0){ + const payload = data.data + if (payload && Array.isArray(payload.items)) { + const items = payload.items as MenuItem[] + setData(items) + setParents(items.filter((m: MenuItem) => m.type !== 3)) + setTotal(Number(payload.total ?? items.length ?? 0)) + setPage(Number(payload.page ?? p)) + setPageSize(Number(payload.page_size ?? ps)) + } else { + const list = (payload || []) as MenuItem[] + setData(list) + setParents(list.filter((m: MenuItem) => m.type !== 3)) + setTotal(Array.isArray(list) ? list.length : 0) + } + } else { throw new Error(data?.message || '获取菜单失败') } }catch(e: any){ message.error(e.message || '获取菜单失败') } finally { setLoading(false) } } - useEffect(() => { fetchMenus(keyword) }, []) + useEffect(() => { fetchMenus(keyword, 1, pageSize) }, []) const onCreate = () => { form.resetFields(); setCreateOpen(true) } const handleCreate = async () => { @@ -446,7 +463,7 @@ export default function Menus(){
-
{ const kw = String(vals?.keyword ?? '').trim(); setKeyword(kw); fetchMenus(kw) }} style={{ marginBottom: 12 }}> + { const kw = String(vals?.keyword ?? '').trim(); setKeyword(kw); fetchMenus(kw, 1, pageSize) }} style={{ marginBottom: 12 }}> @@ -466,7 +483,16 @@ export default function Menus(){ dataSource={treeDataForTable} columns={columns} expandable={{ expandedRowKeys, onExpandedRowsChange: (keys) => setExpandedRowKeys(keys as React.Key[]), indentSize: 18, rowExpandable: (record: MenuItem) => Array.isArray(record.children) && record.children.length > 0 }} - pagination={{ pageSize: 9999, hideOnSinglePage: true }} + // 展示分页统计:显示总条目数(与流程日志一致的受控分页) + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + pageSizeOptions: [10, 20, 50, 100], + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p, ps) => { setPage(p); if (ps) setPageSize(ps); fetchMenus(keyword, p, ps ?? pageSize) } + }} /> setCreateOpen(false)} okText="创建" width={840}> diff --git a/frontend/src/pages/Permissions.tsx b/frontend/src/pages/Permissions.tsx index 6b2a713..ef026d3 100644 --- a/frontend/src/pages/Permissions.tsx +++ b/frontend/src/pages/Permissions.tsx @@ -116,7 +116,7 @@ export default function Permissions(){ loading={loading} dataSource={data} columns={columns} - pagination={{ current: page, pageSize, total, showSizeChanger: true, onChange: (p: number, ps?: number) => fetchPermissions(p, ps ?? pageSize, keyword) }} + pagination={{ current: page, pageSize, total, showSizeChanger: true, pageSizeOptions: [10, 20, 50, 100], onChange: (p: number, ps?: number) => fetchPermissions(p, ps ?? pageSize, keyword) }} /> setCreateOpen(false)} okText="创建" width={840}> diff --git a/frontend/src/pages/Positions.tsx b/frontend/src/pages/Positions.tsx index 90ed02e..5558a87 100644 --- a/frontend/src/pages/Positions.tsx +++ b/frontend/src/pages/Positions.tsx @@ -125,7 +125,16 @@ export default function Positions(){ loading={loading} dataSource={data} columns={columns} - pagination={{ current: page, pageSize, total, showSizeChanger: true, onChange: (p: number, ps?: number) => fetchPositions(p, ps ?? pageSize, keyword) }} + // 展示分页统计:在分页栏显示总条目数 + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + pageSizeOptions: [10, 20, 50, 100], + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p: number, ps?: number) => fetchPositions(p, ps ?? pageSize, keyword) + }} /> setCreateOpen(false)} okText="创建" width={640}> diff --git a/frontend/src/pages/Roles.tsx b/frontend/src/pages/Roles.tsx index 737ee86..7e1b0b1 100644 --- a/frontend/src/pages/Roles.tsx +++ b/frontend/src/pages/Roles.tsx @@ -239,7 +239,16 @@ export default function Roles(){ loading={loading} dataSource={data} columns={columns} - pagination={{ current: page, pageSize, total, showSizeChanger: true, onChange: (p: number, ps?: number) => fetchRoles(p, ps ?? pageSize, keyword) }} + // 展示分页统计:在分页栏显示总条目数 + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + pageSizeOptions: [10, 20, 50, 100], + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p: number, ps?: number) => fetchRoles(p, ps ?? pageSize, keyword) + }} /> setCreateOpen(false)} okText="创建" width={840}> diff --git a/frontend/src/pages/ScheduleJobs.tsx b/frontend/src/pages/ScheduleJobs.tsx index cd711fa..a0fd139 100644 --- a/frontend/src/pages/ScheduleJobs.tsx +++ b/frontend/src/pages/ScheduleJobs.tsx @@ -236,12 +236,15 @@ export default function ScheduleJobs() { loading={loading} dataSource={data} columns={columns} + // 展示分页统计:在分页栏显示总条目数 pagination={{ current: page, pageSize, total, showSizeChanger: true, - onChange: (p, ps) => fetchJobs(p, ps), + pageSizeOptions: [10, 20, 50, 100], + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p: number, ps?: number) => fetchJobs(p, ps ?? pageSize) }} /> diff --git a/frontend/src/pages/Users.tsx b/frontend/src/pages/Users.tsx index 7cf22b3..b741aea 100644 --- a/frontend/src/pages/Users.tsx +++ b/frontend/src/pages/Users.tsx @@ -343,8 +343,16 @@ export default function Users(){ rowKey="id" loading={loading} dataSource={data} - columns={columns} - pagination={{ current: page, pageSize, total, showSizeChanger: true, onChange: (p: number, ps?: number) => fetchUsers(p, ps ?? pageSize, keyword) }} + columns={columns as any} + // 展示分页统计:在分页栏显示总条目数 + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + showTotal: (t, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${t} 条`, + onChange: (p: number, ps?: number) => fetchUsers(p, ps ?? pageSize, keyword) + }} /> {/* 提示:此页面已支持分页、创建、编辑与重置密码。