From 818e34682c17255dbd6b31846a11b79b26accdc7 Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sun, 20 Jul 2025 02:27:33 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20refactor:=20move=20table=20paginati?= =?UTF-8?q?on=20to=20CardPro=20footer=20for=20consistent=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement unified pagination system by moving pagination from CardTable to CardPro footer area, ensuring consistent visual layout across all table pages. ## Changes Made ### Core Components - **CardPro**: Add `paginationArea` prop to display pagination in card footer - **CardTable**: Add `hidePagination` prop to control internal pagination visibility - **utils.js**: Add `createCardProPagination` helper with responsive design - Mobile: small size + showQuickJumper + showTotal - Desktop: default size + showTotal only ### Table Pages Updated - Users table (type1): Add external pagination control - Channels table (type3): Move pagination to CardPro footer - Tokens table (type1): Implement unified pagination layout - Redemptions table (type1): Apply consistent pagination pattern - Usage-logs table (type2): Migrate to external pagination - MJ-logs table (type2): Update pagination configuration - Task-logs table (type2): Standardize pagination approach ### Bug Fixes - Fix CardTable desktop pagination visibility when hidePagination=true - Standardize data access pattern across all table components - Remove redundant data destructuring in users table for consistency ## Benefits - ✅ Consistent pagination position across all tables - ✅ Better visual hierarchy with fixed footer pagination - ✅ Responsive design optimized for mobile and desktop - ✅ Unified codebase with reusable pagination utility - ✅ Backward compatible with existing table functionality ## Files Modified - `web/src/components/common/ui/CardPro.js` - `web/src/components/common/ui/CardTable.js` - `web/src/helpers/utils.js` - `web/src/components/table/*/index.jsx` (7 tables) - `web/src/components/table/*/*.jsx` (7 table components) --- web/src/components/common/ui/CardPro.js | 19 +++++++++- web/src/components/common/ui/CardTable.js | 21 ++++++++--- .../table/channels/ChannelsTable.jsx | 1 + web/src/components/table/channels/index.jsx | 8 +++++ .../components/table/mj-logs/MjLogsTable.jsx | 1 + web/src/components/table/mj-logs/index.jsx | 8 +++++ .../table/redemptions/RedemptionsTable.jsx | 1 + .../components/table/redemptions/index.jsx | 8 +++++ .../table/task-logs/TaskLogsTable.jsx | 1 + web/src/components/table/task-logs/index.jsx | 8 +++++ .../components/table/tokens/TokensTable.jsx | 1 + web/src/components/table/tokens/index.jsx | 8 +++++ .../table/usage-logs/UsageLogsTable.jsx | 1 + web/src/components/table/usage-logs/index.jsx | 8 +++++ web/src/components/table/users/UsersTable.jsx | 1 + web/src/components/table/users/index.jsx | 8 +++++ web/src/helpers/utils.js | 35 ++++++++++++++++++- 17 files changed, 132 insertions(+), 6 deletions(-) diff --git a/web/src/components/common/ui/CardPro.js b/web/src/components/common/ui/CardPro.js index 3325381c..4488661c 100644 --- a/web/src/components/common/ui/CardPro.js +++ b/web/src/components/common/ui/CardPro.js @@ -28,12 +28,13 @@ const { Text } = Typography; /** * CardPro 高级卡片组件 * - * 布局分为5个区域: + * 布局分为6个区域: * 1. 统计信息区域 (statsArea) * 2. 描述信息区域 (descriptionArea) * 3. 类型切换/标签区域 (tabsArea) * 4. 操作按钮区域 (actionsArea) * 5. 搜索表单区域 (searchArea) + * 6. 分页区域 (paginationArea) - 固定在卡片底部 * * 支持三种布局类型: * - type1: 操作型 (如TokensTable) - 描述信息 + 操作按钮 + 搜索表单 @@ -50,6 +51,7 @@ const CardPro = ({ tabsArea, actionsArea, searchArea, + paginationArea, // 新增分页区域 // 卡片属性 shadows = 'always', bordered = false, @@ -159,10 +161,24 @@ const CardPro = ({ const headerContent = renderHeader(); + // 渲染分页区域 + const renderFooter = () => { + if (!paginationArea) return null; + + return ( +
+ {paginationArea} +
+ ); + }; + + const footerContent = renderFooter(); + return ( { +const CardTable = ({ + columns = [], + dataSource = [], + loading = false, + rowKey = 'key', + hidePagination = false, // 新增参数,控制是否隐藏内部分页 + ...tableProps +}) => { const isMobile = useIsMobile(); const { t } = useTranslation(); @@ -62,13 +69,18 @@ const CardTable = ({ columns = [], dataSource = [], loading = false, rowKey = 'k // 如果不是移动端,直接渲染原 Table if (!isMobile) { + // 如果要隐藏分页,则从tableProps中移除pagination + const finalTableProps = hidePagination + ? { ...tableProps, pagination: false } + : tableProps; + return ( ); } @@ -215,8 +227,8 @@ const CardTable = ({ columns = [], dataSource = [], loading = false, rowKey = 'k {dataSource.map((record, index) => ( ))} - {/* 分页组件 */} - {tableProps.pagination && dataSource.length > 0 && ( + {/* 分页组件 - 只在不隐藏分页且有pagination配置时显示 */} + {!hidePagination && tableProps.pagination && dataSource.length > 0 && (
@@ -230,6 +242,7 @@ CardTable.propTypes = { dataSource: PropTypes.array, loading: PropTypes.bool, rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + hidePagination: PropTypes.bool, // 控制是否隐藏内部分页 }; export default CardTable; \ No newline at end of file diff --git a/web/src/components/table/channels/ChannelsTable.jsx b/web/src/components/table/channels/ChannelsTable.jsx index e0270558..bf4d24de 100644 --- a/web/src/components/table/channels/ChannelsTable.jsx +++ b/web/src/components/table/channels/ChannelsTable.jsx @@ -129,6 +129,7 @@ const ChannelsTable = (channelsData) => { onPageSizeChange: handlePageSizeChange, onPageChange: handlePageChange, }} + hidePagination={true} expandAllRows={false} onRow={handleRow} rowSelection={ diff --git a/web/src/components/table/channels/index.jsx b/web/src/components/table/channels/index.jsx index 91dd3200..b29be9fe 100644 --- a/web/src/components/table/channels/index.jsx +++ b/web/src/components/table/channels/index.jsx @@ -29,6 +29,7 @@ import ModelTestModal from './modals/ModelTestModal.jsx'; import ColumnSelectorModal from './modals/ColumnSelectorModal.jsx'; import EditChannelModal from './modals/EditChannelModal.jsx'; import EditTagModal from './modals/EditTagModal.jsx'; +import { createCardProPagination } from '../../../helpers/utils'; const ChannelsPage = () => { const channelsData = useChannelsData(); @@ -58,6 +59,13 @@ const ChannelsPage = () => { tabsArea={} actionsArea={} searchArea={} + paginationArea={createCardProPagination({ + currentPage: channelsData.activePage, + pageSize: channelsData.pageSize, + total: channelsData.channelCount, + onPageChange: channelsData.handlePageChange, + onPageSizeChange: channelsData.handlePageSizeChange, + })} t={channelsData.t} > diff --git a/web/src/components/table/mj-logs/MjLogsTable.jsx b/web/src/components/table/mj-logs/MjLogsTable.jsx index 5b1cfa92..31a2d10e 100644 --- a/web/src/components/table/mj-logs/MjLogsTable.jsx +++ b/web/src/components/table/mj-logs/MjLogsTable.jsx @@ -109,6 +109,7 @@ const MjLogsTable = (mjLogsData) => { onPageSizeChange: handlePageSizeChange, onPageChange: handlePageChange, }} + hidePagination={true} /> ); }; diff --git a/web/src/components/table/mj-logs/index.jsx b/web/src/components/table/mj-logs/index.jsx index 3b0560b8..3d352706 100644 --- a/web/src/components/table/mj-logs/index.jsx +++ b/web/src/components/table/mj-logs/index.jsx @@ -26,6 +26,7 @@ import MjLogsFilters from './MjLogsFilters.jsx'; import ColumnSelectorModal from './modals/ColumnSelectorModal.jsx'; import ContentModal from './modals/ContentModal.jsx'; import { useMjLogsData } from '../../../hooks/mj-logs/useMjLogsData.js'; +import { createCardProPagination } from '../../../helpers/utils'; const MjLogsPage = () => { const mjLogsData = useMjLogsData(); @@ -41,6 +42,13 @@ const MjLogsPage = () => { type="type2" statsArea={} searchArea={} + paginationArea={createCardProPagination({ + currentPage: mjLogsData.activePage, + pageSize: mjLogsData.pageSize, + total: mjLogsData.logCount, + onPageChange: mjLogsData.handlePageChange, + onPageSizeChange: mjLogsData.handlePageSizeChange, + })} t={mjLogsData.t} > diff --git a/web/src/components/table/redemptions/RedemptionsTable.jsx b/web/src/components/table/redemptions/RedemptionsTable.jsx index 58fc5444..76e50532 100644 --- a/web/src/components/table/redemptions/RedemptionsTable.jsx +++ b/web/src/components/table/redemptions/RedemptionsTable.jsx @@ -107,6 +107,7 @@ const RedemptionsTable = (redemptionsData) => { onPageSizeChange: redemptionsData.handlePageSizeChange, onPageChange: handlePageChange, }} + hidePagination={true} loading={loading} rowSelection={rowSelection} onRow={handleRow} diff --git a/web/src/components/table/redemptions/index.jsx b/web/src/components/table/redemptions/index.jsx index 1886c59f..cde9c00f 100644 --- a/web/src/components/table/redemptions/index.jsx +++ b/web/src/components/table/redemptions/index.jsx @@ -25,6 +25,7 @@ import RedemptionsFilters from './RedemptionsFilters.jsx'; import RedemptionsDescription from './RedemptionsDescription.jsx'; import EditRedemptionModal from './modals/EditRedemptionModal'; import { useRedemptionsData } from '../../../hooks/redemptions/useRedemptionsData'; +import { createCardProPagination } from '../../../helpers/utils'; const RedemptionsPage = () => { const redemptionsData = useRedemptionsData(); @@ -99,6 +100,13 @@ const RedemptionsPage = () => { } + paginationArea={createCardProPagination({ + currentPage: redemptionsData.activePage, + pageSize: redemptionsData.pageSize, + total: redemptionsData.tokenCount, + onPageChange: redemptionsData.handlePageChange, + onPageSizeChange: redemptionsData.handlePageSizeChange, + })} t={t} > diff --git a/web/src/components/table/task-logs/TaskLogsTable.jsx b/web/src/components/table/task-logs/TaskLogsTable.jsx index c148709c..cacb12dd 100644 --- a/web/src/components/table/task-logs/TaskLogsTable.jsx +++ b/web/src/components/table/task-logs/TaskLogsTable.jsx @@ -106,6 +106,7 @@ const TaskLogsTable = (taskLogsData) => { onPageSizeChange: handlePageSizeChange, onPageChange: handlePageChange, }} + hidePagination={true} /> ); }; diff --git a/web/src/components/table/task-logs/index.jsx b/web/src/components/table/task-logs/index.jsx index 944f49df..997f3164 100644 --- a/web/src/components/table/task-logs/index.jsx +++ b/web/src/components/table/task-logs/index.jsx @@ -26,6 +26,7 @@ import TaskLogsFilters from './TaskLogsFilters.jsx'; import ColumnSelectorModal from './modals/ColumnSelectorModal.jsx'; import ContentModal from './modals/ContentModal.jsx'; import { useTaskLogsData } from '../../../hooks/task-logs/useTaskLogsData.js'; +import { createCardProPagination } from '../../../helpers/utils'; const TaskLogsPage = () => { const taskLogsData = useTaskLogsData(); @@ -41,6 +42,13 @@ const TaskLogsPage = () => { type="type2" statsArea={} searchArea={} + paginationArea={createCardProPagination({ + currentPage: taskLogsData.activePage, + pageSize: taskLogsData.pageSize, + total: taskLogsData.logCount, + onPageChange: taskLogsData.handlePageChange, + onPageSizeChange: taskLogsData.handlePageSizeChange, + })} t={taskLogsData.t} > diff --git a/web/src/components/table/tokens/TokensTable.jsx b/web/src/components/table/tokens/TokensTable.jsx index 237d05ae..15be1c63 100644 --- a/web/src/components/table/tokens/TokensTable.jsx +++ b/web/src/components/table/tokens/TokensTable.jsx @@ -99,6 +99,7 @@ const TokensTable = (tokensData) => { onPageSizeChange: handlePageSizeChange, onPageChange: handlePageChange, }} + hidePagination={true} loading={loading} rowSelection={rowSelection} onRow={handleRow} diff --git a/web/src/components/table/tokens/index.jsx b/web/src/components/table/tokens/index.jsx index 35ff6102..7011eb7c 100644 --- a/web/src/components/table/tokens/index.jsx +++ b/web/src/components/table/tokens/index.jsx @@ -25,6 +25,7 @@ import TokensFilters from './TokensFilters.jsx'; import TokensDescription from './TokensDescription.jsx'; import EditTokenModal from './modals/EditTokenModal'; import { useTokensData } from '../../../hooks/tokens/useTokensData'; +import { createCardProPagination } from '../../../helpers/utils'; const TokensPage = () => { const tokensData = useTokensData(); @@ -101,6 +102,13 @@ const TokensPage = () => { } + paginationArea={createCardProPagination({ + currentPage: tokensData.activePage, + pageSize: tokensData.pageSize, + total: tokensData.tokenCount, + onPageChange: tokensData.handlePageChange, + onPageSizeChange: tokensData.handlePageSizeChange, + })} t={t} > diff --git a/web/src/components/table/usage-logs/UsageLogsTable.jsx b/web/src/components/table/usage-logs/UsageLogsTable.jsx index b089f5cb..2739d3c4 100644 --- a/web/src/components/table/usage-logs/UsageLogsTable.jsx +++ b/web/src/components/table/usage-logs/UsageLogsTable.jsx @@ -120,6 +120,7 @@ const LogsTable = (logsData) => { }, onPageChange: handlePageChange, }} + hidePagination={true} /> ); }; diff --git a/web/src/components/table/usage-logs/index.jsx b/web/src/components/table/usage-logs/index.jsx index d14a2d65..51336bbf 100644 --- a/web/src/components/table/usage-logs/index.jsx +++ b/web/src/components/table/usage-logs/index.jsx @@ -25,6 +25,7 @@ import LogsFilters from './UsageLogsFilters.jsx'; import ColumnSelectorModal from './modals/ColumnSelectorModal.jsx'; import UserInfoModal from './modals/UserInfoModal.jsx'; import { useLogsData } from '../../../hooks/usage-logs/useUsageLogsData.js'; +import { createCardProPagination } from '../../../helpers/utils'; const LogsPage = () => { const logsData = useLogsData(); @@ -40,6 +41,13 @@ const LogsPage = () => { type="type2" statsArea={} searchArea={} + paginationArea={createCardProPagination({ + currentPage: logsData.activePage, + pageSize: logsData.pageSize, + total: logsData.logCount, + onPageChange: logsData.handlePageChange, + onPageSizeChange: logsData.handlePageSizeChange, + })} t={logsData.t} > diff --git a/web/src/components/table/users/UsersTable.jsx b/web/src/components/table/users/UsersTable.jsx index 53ca747e..cd93bf95 100644 --- a/web/src/components/table/users/UsersTable.jsx +++ b/web/src/components/table/users/UsersTable.jsx @@ -137,6 +137,7 @@ const UsersTable = (usersData) => { onPageSizeChange: handlePageSizeChange, onPageChange: handlePageChange, }} + hidePagination={true} loading={loading} onRow={handleRow} empty={ diff --git a/web/src/components/table/users/index.jsx b/web/src/components/table/users/index.jsx index ce282aaf..cc477154 100644 --- a/web/src/components/table/users/index.jsx +++ b/web/src/components/table/users/index.jsx @@ -26,6 +26,7 @@ import UsersDescription from './UsersDescription.jsx'; import AddUserModal from './modals/AddUserModal.jsx'; import EditUserModal from './modals/EditUserModal.jsx'; import { useUsersData } from '../../../hooks/users/useUsersData'; +import { createCardProPagination } from '../../../helpers/utils'; const UsersPage = () => { const usersData = useUsersData(); @@ -104,6 +105,13 @@ const UsersPage = () => { /> } + paginationArea={createCardProPagination({ + currentPage: usersData.activePage, + pageSize: usersData.pageSize, + total: usersData.userCount, + onPageChange: usersData.handlePageChange, + onPageSizeChange: usersData.handlePageSizeChange, + })} t={t} > diff --git a/web/src/helpers/utils.js b/web/src/helpers/utils.js index dffb04d7..244b6058 100644 --- a/web/src/helpers/utils.js +++ b/web/src/helpers/utils.js @@ -17,13 +17,14 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import { Toast } from '@douyinfe/semi-ui'; +import { Toast, Pagination } from '@douyinfe/semi-ui'; import { toastConstants } from '../constants'; import React from 'react'; import { toast } from 'react-toastify'; import { THINK_TAG_REGEX, MESSAGE_ROLES } from '../constants/playground.constants'; import { TABLE_COMPACT_MODES_KEY } from '../constants'; import { MOBILE_BREAKPOINT } from '../hooks/common/useIsMobile.js'; +import { useIsMobile } from '../hooks/common/useIsMobile.js'; const HTMLToastContent = ({ htmlContent }) => { return
; @@ -567,3 +568,35 @@ export const modelSelectFilter = (input, option) => { const val = (option?.value || '').toString().toLowerCase(); return val.includes(input.trim().toLowerCase()); }; + +// ------------------------------- +// CardPro 分页配置组件 +// 用于创建 CardPro 的 paginationArea 配置 +export const createCardProPagination = ({ + currentPage, + pageSize, + total, + onPageChange, + onPageSizeChange, + pageSizeOpts = [10, 20, 50, 100], + showSizeChanger = true, +}) => { + const isMobile = useIsMobile(); + + if (!total || total <= 0) return null; + + return ( + + ); +};