feat(datagrid): 增加分页跳页并适配窄屏

- 分页条新增跳页输入与提交按钮,支持回车和点击跳转

- 跳页页码自动限制在有效页码范围内,避免越界分页请求

- 为 v2 状态栏增加容器级响应式规则,适配 AI 面板打开后的窄宽场景

- 分页区域增加横向滚动兜底,避免小尺寸屏幕下控件被挤压变形

- 补充 DataGrid 布局回归测试,覆盖跳页控件和窄屏样式规则
This commit is contained in:
Syngnat
2026-06-01 11:56:56 +08:00
parent b85e7491a9
commit 09139c2553
4 changed files with 256 additions and 5 deletions

View File

@@ -104,6 +104,9 @@ describe('DataGrid layout', () => {
expect(markup).toContain('data-grid-v2-page-chip="true"');
expect(markup).toContain('data-grid-v2-pagination-prev="true"');
expect(markup).toContain('data-grid-v2-pagination-next="true"');
expect(markup).toContain('data-grid-pagination-jump="true"');
expect(markup).toContain('跳页');
expect(markup).toContain('跳转页码');
expect(markup).not.toContain('class="ant-pagination');
expect(markup).not.toContain('class="data-grid-pagination-kicker"');
expect(markup).toContain('当前页查找...');
@@ -545,9 +548,19 @@ describe('DataGrid layout', () => {
expect(secondaryActionsSource.indexOf('{pageFindContent}')).toBeLessThan(secondaryActionsSource.indexOf('gn-v2-data-grid-status-center'));
expect(css).toContain('width: 66px !important;');
expect(css).toContain('grid-template-columns: 160px 26px 26px !important;');
expect(css).toContain('container-name: gn-v2-data-grid-statusbar;');
expect(css).toContain('body[data-ui-version="v2"] .gn-v2-data-grid-status-right::-webkit-scrollbar');
expect(css).toContain('body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap::-webkit-scrollbar');
expect(css).toContain('@container gn-v2-data-grid-statusbar (max-width: 960px)');
expect(css).toContain('@container gn-v2-data-grid-statusbar (max-width: 760px)');
expect(css).toContain('.data-grid-pagination-size-select.ant-select-focused .ant-select-selector');
expect(css).toContain('overflow-x: auto;');
expect(paginationBarSource).toContain("label: `${value}/页`");
expect(paginationBarSource).toContain('const maxJumpPage = Math.max(1, paginationTotalPages);');
expect(paginationBarSource).toContain('Math.min(maxJumpPage, Math.max(1, Math.trunc(Number(jumpPage))))');
expect(paginationBarSource).toContain('onPressEnter={submitJumpPage}');
expect(paginationBarSource).toContain('data-grid-pagination-jump="true"');
expect(css).toContain('.data-grid-pagination-jump-input.ant-input-number-focused');
expect(css).toContain('background: transparent !important;');
});

View File

@@ -2913,6 +2913,57 @@ const DataGrid: React.FC<DataGridProps> = ({
width: 100%;
height: 100%;
}
.${gridId} .data-grid-pagination-jump {
display: inline-flex;
align-items: center;
gap: 6px;
height: 34px;
color: ${paginationSecondaryTextColor};
font-size: 12px;
font-weight: 600;
white-space: nowrap;
}
.${gridId} .data-grid-pagination-jump-label {
color: ${paginationSecondaryTextColor};
font-variant-numeric: tabular-nums;
}
.${gridId} .data-grid-pagination-jump-input,
.${gridId} .data-grid-pagination-jump-input.ant-input-number {
width: 64px;
min-width: 64px;
height: 34px;
display: inline-flex;
align-items: stretch;
}
.${gridId} .data-grid-pagination-jump-input .ant-input-number-input-wrap,
.${gridId} .data-grid-pagination-jump-input .ant-input-number-input {
height: 100%;
}
.${gridId} .data-grid-pagination-jump-input .ant-input-number-input {
padding: 0 10px;
text-align: center;
color: ${paginationPrimaryTextColor};
font-weight: 600;
font-variant-numeric: tabular-nums;
line-height: 34px;
}
.${gridId} .data-grid-pagination-jump-input.ant-input-number {
border-radius: 12px;
border: 1px solid ${paginationChipBorderColor};
background: ${paginationChipBg};
box-shadow: none;
}
.${gridId} .data-grid-pagination-jump-button.ant-btn {
height: 34px;
min-width: 34px;
padding: 0 10px;
border-radius: 12px;
border-color: ${paginationChipBorderColor};
background: ${paginationChipBg};
color: ${paginationPrimaryTextColor};
font-weight: 700;
box-shadow: none;
}
.${gridId} .data-grid-pagination-size-select {
width: 72px;
min-width: 72px;

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Button, Pagination, Select } from 'antd';
import { Button, InputNumber, Pagination, Select } from 'antd';
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
interface DataGridPaginationState {
@@ -38,10 +38,53 @@ const DataGridPaginationBar: React.FC<DataGridPaginationBarProps> = ({
onPageSizeChange,
onV2PageStep,
}) => {
const [jumpPage, setJumpPage] = React.useState<number | null>(pagination?.current ?? null);
React.useEffect(() => {
setJumpPage(pagination?.current ?? null);
}, [pagination?.current]);
if (!pagination) {
return null;
}
const maxJumpPage = Math.max(1, paginationTotalPages);
const normalizedJumpPage = Number.isFinite(Number(jumpPage)) && Number(jumpPage) > 0
? Math.min(maxJumpPage, Math.max(1, Math.trunc(Number(jumpPage))))
: null;
const jumpDisabled = !onPageChange || normalizedJumpPage === null || normalizedJumpPage === pagination.current;
const submitJumpPage = () => {
if (!onPageChange || normalizedJumpPage === null) return;
if (normalizedJumpPage === pagination.current) return;
onPageChange(normalizedJumpPage, pagination.pageSize);
};
const jumpPageControl = (
<div className="data-grid-pagination-jump" data-grid-pagination-jump="true">
<span className="data-grid-pagination-jump-label"></span>
<InputNumber
size="small"
min={1}
max={maxJumpPage}
precision={0}
controls={false}
value={jumpPage}
onChange={(value) => setJumpPage(typeof value === 'number' && Number.isFinite(value) ? value : null)}
onPressEnter={submitJumpPage}
className="data-grid-pagination-jump-input"
aria-label="跳转页码"
disabled={!onPageChange}
/>
<Button
size="small"
className="data-grid-pagination-jump-button"
disabled={jumpDisabled}
onClick={submitJumpPage}
>
</Button>
</div>
);
return (
<div
className={`${isV2Ui ? 'gn-v2-data-grid-pagination-wrap ' : ''}data-grid-pagination-wrap`}
@@ -71,6 +114,7 @@ const DataGridPaginationBar: React.FC<DataGridPaginationBarProps> = ({
disabled={!onPageChange || pagination.current >= paginationTotalPages}
onClick={() => onV2PageStep('next')}
/>
{jumpPageControl}
<Select
size="small"
popupMatchSelectWidth={false}
@@ -105,6 +149,7 @@ const DataGridPaginationBar: React.FC<DataGridPaginationBarProps> = ({
return originalElement;
}}
/>
{jumpPageControl}
<Select
size="small"
popupMatchSelectWidth={false}