mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-12 17:39:42 +08:00
✨ feat(datagrid): 增加分页跳页并适配窄屏
- 分页条新增跳页输入与提交按钮,支持回车和点击跳转 - 跳页页码自动限制在有效页码范围内,避免越界分页请求 - 为 v2 状态栏增加容器级响应式规则,适配 AI 面板打开后的窄宽场景 - 分页区域增加横向滚动兜底,避免小尺寸屏幕下控件被挤压变形 - 补充 DataGrid 布局回归测试,覆盖跳页控件和窄屏样式规则
This commit is contained in:
@@ -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;');
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -3291,6 +3291,8 @@ body[data-ui-version="v2"] .gn-v2-data-grid .data-grid-virtual-inline-editing .a
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-statusbar {
|
||||
--gn-v2-statusbar-control-height: 26px;
|
||||
container-type: inline-size;
|
||||
container-name: gn-v2-data-grid-statusbar;
|
||||
height: calc(36px * var(--gn-ui-scale, 1));
|
||||
min-height: calc(36px * var(--gn-ui-scale, 1));
|
||||
display: flex;
|
||||
@@ -3325,9 +3327,17 @@ body[data-ui-version="v2"] .gn-v2-data-grid-status-right {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex: 0 0 auto;
|
||||
min-width: fit-content;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
margin-left: auto;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-status-right::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
@@ -3749,17 +3759,27 @@ body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap {
|
||||
height: var(--gn-v2-statusbar-control-height);
|
||||
min-height: var(--gn-v2-statusbar-control-height);
|
||||
gap: 4px;
|
||||
flex: 0 0 auto;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
margin-left: 0;
|
||||
padding: 0 !important;
|
||||
border-top: none !important;
|
||||
background: transparent;
|
||||
justify-content: flex-end !important;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid .data-grid-pagination-shell {
|
||||
gap: 4px;
|
||||
flex: 0 0 auto;
|
||||
flex: 0 0 max-content;
|
||||
min-width: max-content;
|
||||
align-items: center !important;
|
||||
align-self: center;
|
||||
justify-content: flex-start !important;
|
||||
@@ -3896,6 +3916,128 @@ body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-paginatio
|
||||
color: var(--gn-fg-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
height: 26px;
|
||||
min-height: 26px;
|
||||
color: var(--gn-fg-4);
|
||||
font-family: var(--gn-font-mono);
|
||||
font-size: 11px;
|
||||
line-height: 26px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-label {
|
||||
color: var(--gn-fg-4);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input.ant-input-number,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-button.ant-btn {
|
||||
height: 26px !important;
|
||||
min-height: 26px !important;
|
||||
border-width: 0.5px !important;
|
||||
border-style: solid !important;
|
||||
border-color: var(--gn-br-2) !important;
|
||||
border-radius: 5px !important;
|
||||
background: var(--gn-bg-input) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input.ant-input-number {
|
||||
display: inline-flex !important;
|
||||
align-items: stretch !important;
|
||||
width: 44px !important;
|
||||
min-width: 44px !important;
|
||||
max-width: 44px !important;
|
||||
line-height: 26px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input .ant-input-number-input-wrap,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input .ant-input-number-input {
|
||||
height: 26px !important;
|
||||
line-height: 26px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input .ant-input-number-input {
|
||||
padding: 0 4px !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
font-family: var(--gn-font-mono);
|
||||
font-size: 11px !important;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input.ant-input-number-focused,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input.ant-input-number:focus-within {
|
||||
border-color: color-mix(in srgb, var(--gn-info) 38%, var(--gn-br-2)) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-button.ant-btn {
|
||||
width: 26px !important;
|
||||
min-width: 26px !important;
|
||||
padding: 0 !important;
|
||||
color: var(--gn-fg-2) !important;
|
||||
font-size: 11px !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@container gn-v2-data-grid-statusbar (max-width: 1120px) {
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-summary {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container gn-v2-data-grid-statusbar (max-width: 960px) {
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-page-find {
|
||||
max-width: 154px;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-page-find .ant-input-affix-wrapper {
|
||||
width: 112px !important;
|
||||
min-width: 112px;
|
||||
max-width: 112px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-jump-input.ant-input-number {
|
||||
width: 38px !important;
|
||||
min-width: 38px !important;
|
||||
max-width: 38px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@container gn-v2-data-grid-statusbar (max-width: 760px) {
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-result-switcher > span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-column-quick-find,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-column-quick-find .ant-select,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-column-quick-find .ant-input-affix-wrapper,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-column-quick-find .ant-select-selector {
|
||||
width: 140px !important;
|
||||
min-width: 140px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-column-quick-find {
|
||||
grid-template-columns: 140px 28px !important;
|
||||
max-width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-size-select,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-size-select.ant-select,
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-pagination-wrap .data-grid-pagination-size-select.ant-select-single,
|
||||
|
||||
Reference in New Issue
Block a user