fix(web): offset sticky table headers below layout header

This commit is contained in:
2026-10-06 14:22:47 +08:00
parent 3d668769f6
commit ca3cbb8d37
8 changed files with 68 additions and 14 deletions
@@ -10,7 +10,7 @@ import { PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { remove, creatorUpdate, creatorGet, creatorAction } from '@/services/api';
import { accountReadiness, conflictMessage, dateTime } from '@/utils/helpers';
import { fixedLeft, fixedRight, tablePagination } from '@/utils/table';
import { fixedLeft, fixedRight, tablePagination, useTableSticky } from '@/utils/table';
import AccountLoginModal from '@/components/accounts/AccountLoginModal';
import AccountCreateModal from '@/components/accounts/AccountCreateModal';
@@ -70,6 +70,7 @@ export default function AccountManagementList() {
const [actionBusy, setActionBusy] = useState('');
const [loginTarget, setLoginTarget] = useState<Row | null>(null);
const { message: messageApi } = App.useApp();
const sticky = useTableSticky();
const load = useCallback(async () => {
setPending(true);
@@ -333,7 +334,7 @@ export default function AccountManagementList() {
columns={columns}
dataSource={rows}
loading={pending}
sticky
sticky={sticky}
pagination={tablePagination()}
scroll={{ x: 'max-content' }}
locale={{ emptyText: '暂无账号或待登录环境。' }}
@@ -6,7 +6,7 @@ import type { ColumnsType } from 'antd/es/table';
import { CloudUploadOutlined, ReloadOutlined } from '@ant-design/icons';
import { getList, remove, creatorAction, creatorUpdate } from '@/services/api';
import { conflictMessage, dateTime } from '@/utils/helpers';
import { fixedLeft, fixedRight, tablePagination } from '@/utils/table';
import { fixedLeft, fixedRight, tablePagination, useTableSticky } from '@/utils/table';
interface CompetitorRow {
id: string;
@@ -46,6 +46,7 @@ export default function MonitoringAccountList() {
const [actionBusy, setActionBusy] = useState('');
const { message: messageApi } = App.useApp();
const horizontal = !Grid.useBreakpoint().xl;
const sticky = useTableSticky();
const load = useCallback(async () => {
setPending(true);
@@ -227,7 +228,7 @@ export default function MonitoringAccountList() {
columns={columns}
dataSource={rows}
loading={pending}
sticky
sticky={sticky}
pagination={tablePagination()}
locale={{ emptyText: '暂无监控账号。' }}
scroll={{ x: horizontal ? 1100 : undefined }}
+3 -2
View File
@@ -5,7 +5,7 @@ import { PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { create, getList, creatorAction } from '@/services/api';
import { conflictMessage, dateTime, extractShareURL, platformForShareURL, platformLabel, shareJobStatus } from '@/utils/helpers';
import { fixedLeft, fixedRight, tablePagination } from '@/utils/table';
import { fixedLeft, fixedRight, tablePagination, useTableSticky } from '@/utils/table';
interface Job {
id: string;
@@ -35,6 +35,7 @@ export default function Page() {
const [retryID, setRetryID] = useState<string | null>(null);
const { message: messageApi } = App.useApp();
const horizontal = !Grid.useBreakpoint().xl;
const sticky = useTableSticky();
const loadShareJobs = useCallback(async () => {
setPending(true);
@@ -188,7 +189,7 @@ export default function Page() {
{error ? (
<Alert type="error" showIcon style={{ marginBottom: 16 }} title={error.message} action={<Button size="small" onClick={loadShareJobs}>重试</Button>} />
) : null}
<Table rowKey="id" columns={columns} dataSource={jobs} loading={pending} sticky pagination={tablePagination()} scroll={{ x: horizontal ? 1000 : undefined }} locale={{ emptyText: '暂无分享链接解析任务。' }} />
<Table rowKey="id" columns={columns} dataSource={jobs} loading={pending} sticky={sticky} pagination={tablePagination()} scroll={{ x: horizontal ? 1000 : undefined }} locale={{ emptyText: '暂无分享链接解析任务。' }} />
<Modal open={createOpen} title="创建账号导入任务" onCancel={closeCreate} onOk={createJob} confirmLoading={busy} okText={busy ? '加入中…' : '加入解析队列'} width={560}>
<Form form={form} layout="vertical">
<Form.Item name="shareText" label="分享内容或链接" required extra="可直接粘贴平台分享文案,系统会自动提取其中的链接并识别作者。">
+3 -2
View File
@@ -5,7 +5,7 @@ import { CopyOutlined, EyeInvisibleOutlined, EyeOutlined, PlusOutlined } from '@
import type { ColumnsType } from 'antd/es/table';
import { create, getList, remove, update } from '@/services/api';
import { conflictMessage, dateTime } from '@/utils/helpers';
import { fixedLeft, fixedRight, tablePagination } from '@/utils/table';
import { fixedLeft, fixedRight, tablePagination, useTableSticky } from '@/utils/table';
const namePattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
const tokenPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{15,127}$/;
@@ -96,6 +96,7 @@ export default function Page() {
const [created, setCreated] = useState<any>(null);
const { message: messageApi } = App.useApp();
const horizontal = !Grid.useBreakpoint().xl;
const sticky = useTableSticky();
const healthReloadTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
const load = useCallback(async () => {
@@ -234,7 +235,7 @@ export default function Page() {
{error ? (
<Alert type="error" showIcon style={{ marginBottom: 16 }} title={error.message} action={<Button size="small" onClick={load}>重试</Button>} />
) : null}
<Table rowKey="name" columns={columns} dataSource={gateways} loading={pending} sticky pagination={tablePagination()} scroll={{ x: horizontal ? 960 : undefined }} locale={{ emptyText: '暂无网关;注册后环境即可调度到该网关。' }} />
<Table rowKey="name" columns={columns} dataSource={gateways} loading={pending} sticky={sticky} pagination={tablePagination()} scroll={{ x: horizontal ? 960 : undefined }} locale={{ emptyText: '暂无网关;注册后环境即可调度到该网关。' }} />
<GatewayFormModal
open={createOpen}
initial={null}
+3 -2
View File
@@ -6,7 +6,7 @@ import { PlusOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { create, getOne, getList, networkExitAction, remove, update } from '@/services/api';
import { conflictMessage, dateTime } from '@/utils/helpers';
import { fixedLeft, fixedRight, tablePagination } from '@/utils/table';
import { fixedLeft, fixedRight, tablePagination, useTableSticky } from '@/utils/table';
const healthText: Record<string, string> = {
unchecked: '未检测',
@@ -114,6 +114,7 @@ export default function Page() {
const [busy, setBusy] = useState('');
const { message: messageApi } = App.useApp();
const horizontal = !Grid.useBreakpoint().xl;
const sticky = useTableSticky();
const load = useCallback(async () => {
setPending(true);
@@ -304,7 +305,7 @@ export default function Page() {
columns={columns}
dataSource={exits}
loading={pending}
sticky
sticky={sticky}
pagination={tablePagination()}
scroll={{ x: horizontal ? 1100 : undefined }}
locale={{ emptyText: '暂无网络出口。创建并检测健康后,才能绑定运行环境。' }}
+10
View File
@@ -1,9 +1,19 @@
// 列表表格使用页面纵向滚动与 sticky 表头吸顶,不设置表内纵向滚动。
// 保留横向滚动、固定首列/操作列与分页(每页 20 条,单页隐藏分页条)。
import { ProProvider, RouteContext } from '@ant-design/pro-components';
import type { ColumnsType, ColumnType } from 'antd/es/table';
import { useContext } from 'react';
export const TABLE_PAGE_SIZE = 20;
export function useTableSticky() {
const { hasHeader, fixedHeader, layout } = useContext(RouteContext);
const { token } = useContext(ProProvider);
// ProLayout 的 mix 布局默认固定顶部栏;高度与其原生 token/default 保持一致。
const headerHeight = token.layout?.header?.heightLayoutHeader || 56;
return { offsetHeader: hasHeader && (fixedHeader || layout === 'mix') ? headerHeight : 0 };
}
export function tablePagination() {
return { pageSize: TABLE_PAGE_SIZE, hideOnSinglePage: true };
}