feat: 全局分页参数:滚动条修复,前端其他异常警告修复

dev_na
puz 2026-06-23 15:23:46 +08:00
parent b34efa5eb0
commit 5ff2ddc388
15 changed files with 229 additions and 85 deletions

View File

@ -15,3 +15,8 @@
.app-pagination-container .ant-pagination-total-text {
margin-right: auto;
}
.app-pagination-container .ant-select-selection-search-input {
caret-color: transparent;
cursor: pointer;
}

View File

@ -9,18 +9,23 @@ export interface AppPaginationProps extends PaginationProps {
export default function AppPagination(props: AppPaginationProps) {
const { t } = useTranslation();
const mergedClassName = ['app-global-pagination', props.className].filter(Boolean).join(' ');
const { className, showSizeChanger, ...restProps } = props;
const mergedClassName = ['app-global-pagination', className].filter(Boolean).join(' ');
const mergedShowSizeChanger =
showSizeChanger === undefined || showSizeChanger === true
? { showSearch: false }
: showSizeChanger;
return (
<div className="app-pagination-container">
<Pagination
className={mergedClassName}
showSizeChanger
showSizeChanger={mergedShowSizeChanger}
showQuickJumper
showTotal={(total) => t('common.total', { total })}
pageSizeOptions={['8','10', '20', '50', '100']}
size="default"
{...props}
{...restProps}
/>
</div>
);

View File

@ -35,7 +35,7 @@ function ListTable<T extends Record<string, any>>({
onClearSelection,
pagination = {
pageSize: 10,
showSizeChanger: true,
showSizeChanger: { showSearch: false },
showQuickJumper: true,
},
scroll = { x: 1200 },
@ -68,9 +68,16 @@ function ListTable<T extends Record<string, any>>({
const mergedPagination =
pagination === false
? false
: {
: (() => {
const mergedShowSizeChanger =
pagination.showSizeChanger === undefined || pagination.showSizeChanger === true
? { showSearch: false }
: pagination.showSizeChanger;
return {
...pagination,
className: ["app-global-pagination", pagination.className].filter(Boolean).join(" "),
showSizeChanger: mergedShowSizeChanger,
showTotal: (total: number) => (
<div className="table-selection-info">
{isAllPagesSelected ? (
@ -108,6 +115,7 @@ function ListTable<T extends Record<string, any>>({
</div>
),
};
})();
const wrapperStyle = hasVerticalScroll
? ({

View File

@ -12,9 +12,11 @@
--app-bg-overlay-size: 36px 36px;
--app-bg-card: rgba(255, 255, 255, 0.74);
--app-text-main: #1f2937;
--app-text-secondary: #66758f;
--app-border-color: rgba(103, 126, 189, 0.12);
--app-shadow: 0 18px 40px rgba(100, 118, 171, 0.1);
--app-bg-page: rgba(255, 255, 255, 0.18);
--app-bg-surface: rgba(255, 255, 255, 0.68);
--app-bg-surface-soft: rgba(255, 255, 255, 0.56);
--app-bg-surface-strong: rgba(255, 255, 255, 0.82);
--app-text-muted: #66758f;
@ -29,9 +31,11 @@
linear-gradient(90deg, rgba(255, 255, 255, 0.24) 1px, transparent 1px);
--app-bg-card: rgba(255, 255, 255, 0.82);
--app-text-main: #111827;
--app-text-secondary: #5b6474;
--app-border-color: rgba(148, 163, 184, 0.16);
--app-shadow: 0 14px 32px rgba(15, 23, 42, 0.08);
--app-bg-page: rgba(255, 255, 255, 0.16);
--app-bg-surface: rgba(255, 255, 255, 0.74);
--app-bg-surface-soft: rgba(255, 255, 255, 0.62);
--app-bg-surface-strong: rgba(255, 255, 255, 0.86);
--app-text-muted: #5b6474;
@ -47,9 +51,11 @@
linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
--app-bg-card: rgba(13, 23, 39, 0.62);
--app-text-main: #e2e8f0;
--app-text-secondary: rgba(190, 206, 229, 0.74);
--app-border-color: rgba(88, 151, 255, 0.18);
--app-shadow: 0 18px 44px rgba(0, 0, 0, 0.34);
--app-bg-page: rgba(5, 12, 24, 0.22);
--app-bg-surface: rgba(10, 21, 37, 0.78);
--app-bg-surface-soft: rgba(10, 21, 37, 0.72);
--app-bg-surface-strong: rgba(8, 17, 31, 0.88);
--app-text-muted: rgba(190, 206, 229, 0.74);
@ -237,10 +243,14 @@ body::after {
flex: 1;
min-height: 0;
min-width: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.app-page__table-wrap .ant-table-wrapper {
flex: 1;
min-height: 0;
min-width: 0;
}
@ -863,7 +873,6 @@ body::after {
.ant-table-wrapper .ant-table-body {
overflow-y: auto !important;
max-height: none !important;
}
.ant-table-wrapper .ant-table-pagination.ant-pagination.app-global-pagination,
@ -898,6 +907,12 @@ body::after {
margin-inline-start: 12px;
}
.app-global-pagination.ant-pagination .ant-select-selection-search-input,
.ant-table-wrapper .ant-table-pagination.ant-pagination.app-global-pagination .ant-select-selection-search-input {
caret-color: transparent;
cursor: pointer;
}
.meeting-share-popover .ant-popover-inner {
padding: 0;
border-radius: 16px;
@ -1140,7 +1155,7 @@ body::after {
@keyframes ai-ring-expand {
0% { width: 40px; height: 40px; opacity: 0.8; border-width: 2px; }
100% { width: 200px; height: 200px; opacity: 0; border-width: 0px; }
100% { width: 200px; height: 200px; opacity: 0; border-width: 0; }
}
@keyframes ai-core-pulse {
0% { transform: scale(0.8); opacity: 0.3; }

View File

@ -58,10 +58,38 @@ type PermissionMenuNode = SysPermission & {
type CachedUserProfile = { displayName?: string; username?: string; avatarUrl?: string };
type ActiveMenuMatch = {
key: string;
parentKeys: string[];
};
function getAvatarUrl(profile?: CachedUserProfile | null) {
return profile?.avatarUrl?.trim() || "";
}
function getMenuKey(item: Pick<SysPermission, "permId" | "path" | "code">) {
return `${item.path || item.code || "menu"}:${item.permId}`;
}
function findActiveMenu(nodes: PermissionMenuNode[], path: string, parentKeys: string[] = []): ActiveMenuMatch | null {
for (const node of nodes) {
const key = getMenuKey(node);
if (node.path === path) {
return { key, parentKeys };
}
if (node.children?.length) {
const found = findActiveMenu(node.children, path, [...parentKeys, key]);
if (found) {
return found;
}
}
}
return null;
}
export default function AppLayout() {
const { t, i18n } = useTranslation();
const [collapsed, setCollapsed] = useState(false);
@ -100,7 +128,7 @@ export default function AppLayout() {
const location = useLocation();
const navigate = useNavigate();
const { logout } = useAuth();
const { load: loadPermissions, can } = usePermission();
const { load: loadPermissions } = usePermission();
const { layoutMode } = useThemeStore();
const fetchInitialData = useCallback(async () => {
@ -255,7 +283,7 @@ export default function AppLayout() {
const toMenuItems = useCallback((nodes: PermissionMenuNode[]): MenuProps["items"] =>
nodes.map((item) => {
const key = item.path || item.code || String(item.permId);
const key = getMenuKey(item);
const icon = resolveMenuIcon(item.icon);
if (item.permType === "directory" || item.children?.length) {
@ -274,39 +302,18 @@ export default function AppLayout() {
};
}), []);
const menuItems = useMemo(() => toMenuItems(buildMenuTree(menus)), [buildMenuTree, menus, toMenuItems]);
const menuTree = useMemo(() => buildMenuTree(menus), [buildMenuTree, menus]);
const menuItems = useMemo(() => toMenuItems(menuTree), [menuTree, toMenuItems]);
const activeMenu = useMemo(() => findActiveMenu(menuTree, location.pathname), [location.pathname, menuTree]);
const selectedMenuKeys = activeMenu ? [activeMenu.key] : [];
useEffect(() => {
if (!menus.length) {
if (!activeMenu?.parentKeys.length) {
return;
}
const findParentKeys = (nodes: NonNullable<MenuProps["items"]>, path: string, parents: string[] = []): string[] | null => {
for (const node of nodes) {
if (!node || typeof node !== "object" || !("key" in node)) {
continue;
}
if (String(node.key) === path) {
return parents;
}
if ("children" in node && node.children) {
const found = findParentKeys(node.children, path, [...parents, String(node.key)]);
if (found) {
return found;
}
}
}
return null;
};
const keys = findParentKeys(menuItems || [], location.pathname);
if (keys?.length) {
setOpenKeys((prev) => Array.from(new Set([...prev, ...keys])));
}
}, [location.pathname, menuItems, menus.length]);
setOpenKeys((prev) => Array.from(new Set([...prev, ...activeMenu.parentKeys])));
}, [activeMenu]);
const userMenuItems: MenuProps["items"] = useMemo(() => {
const items: NonNullable<MenuProps["items"]> = [
@ -418,7 +425,7 @@ export default function AppLayout() {
<div style={{ flex: 1, overflowY: "auto", overflowX: "hidden" }}>
<Menu
mode="inline"
selectedKeys={[location.pathname]}
selectedKeys={selectedMenuKeys}
openKeys={openKeys}
onOpenChange={setOpenKeys}
items={menuItems}
@ -458,7 +465,7 @@ export default function AppLayout() {
<div style={{ flex: 1, minWidth: 0, padding: "0 24px" }}>
<Menu
mode="horizontal"
selectedKeys={[location.pathname]}
selectedKeys={selectedMenuKeys}
items={menuItems}
style={{
borderBottom: 0,

View File

@ -62,7 +62,6 @@
.ant-table-body {
flex: 1;
min-height: 0;
max-height: none !important;
overflow-y: auto !important;
&::-webkit-scrollbar {

View File

@ -67,6 +67,18 @@ const DraggableRow = ({ children, ...props }: RowProps) => {
);
};
const DragHandle = () => {
const { setActivatorNodeRef, listeners } = React.useContext(DragContext);
return (
<HolderOutlined
ref={setActivatorNodeRef}
style={{ touchAction: 'none', cursor: 'move', color: '#999' }}
{...listeners}
/>
);
};
const menuIconOptions = Object.keys(AntIcons)
.filter((key) => /(?:Outlined|Filled|TwoTone)$/.test(key))
.sort((left, right) => left.localeCompare(right));
@ -331,16 +343,7 @@ export default function Permissions() {
key: 'sortHandle',
width: 40,
align: 'center' as const,
render: () => {
const { setActivatorNodeRef, listeners } = React.useContext(DragContext);
return (
<HolderOutlined
ref={setActivatorNodeRef}
style={{ touchAction: 'none', cursor: 'move', color: '#999' }}
{...listeners}
/>
);
},
render: () => <DragHandle />,
},
{
title: t("permissions.permName"),

View File

@ -444,7 +444,7 @@ const AiModels: React.FC = () => {
columns={columns}
dataSource={data}
loading={loading}
scroll={{ x: "max-content" }}
scroll={{ x: "max-content", y: "100%" }}
pagination={false}
/>
</div>
@ -559,9 +559,7 @@ const AiModels: React.FC = () => {
rules={activeType === "LLM" ? [{ required: true, message: "请输入或选择模型名称" }] : []}
>
<AutoComplete
allowClear
style={{ width: "calc(100% - 100px)" }}
placeholder="可选择或自定义输入模型名称"
onFocus={() => {
if (isLocalProvider && remoteModels.length === 0) {
void handleFetchRemote();
@ -573,7 +571,7 @@ const AiModels: React.FC = () => {
String(option?.value || "").toLowerCase().includes(inputValue.toLowerCase())
}
>
<Input />
<Input allowClear placeholder="可选择或自定义输入模型名称" />
</AutoComplete>
</Form.Item>
<Button icon={<SyncOutlined spin={fetchLoading} />} onClick={handleFetchRemote} style={{ width: 100 }}>

View File

@ -418,7 +418,7 @@ export default function ClientManagement() {
>
<Row gutter={24} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
@ -431,7 +431,7 @@ export default function ClientManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
@ -444,7 +444,7 @@ export default function ClientManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
@ -457,7 +457,7 @@ export default function ClientManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
@ -497,7 +497,7 @@ export default function ClientManagement() {
dataSource={pagedRecords}
loading={loading || groupLoading || platformLoading}
locale={platformGroups.length === 0 ? { emptyText: <Empty description="未配置 client_platform 字典项" /> } : undefined}
scroll={{x: 1100}}
scroll={{ x: 1100, y: "100%" }}
pagination={false}
/>
</div>

View File

@ -303,7 +303,7 @@ export default function ExternalAppManagement() {
>
<Row gutter={24} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
@ -316,7 +316,7 @@ export default function ExternalAppManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
@ -329,7 +329,7 @@ export default function ExternalAppManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}>Web </span>
@ -342,7 +342,7 @@ export default function ExternalAppManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<Card variant="borderless" style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
@ -368,7 +368,7 @@ export default function ExternalAppManagement() {
<Card className="app-page__content-card flex-1 flex flex-col overflow-hidden" styles={{ body: { padding: 0, flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" } }}>
<div className="app-page__table-wrap" style={{ padding: "0 24px", overflow: "auto" }}>
<Table rowKey="id" columns={columns} dataSource={pagedRecords} loading={loading} scroll={{ x: "max-content" }} pagination={false} />
<Table rowKey="id" columns={columns} dataSource={pagedRecords} loading={loading} scroll={{ x: "max-content", y: "100%" }} pagination={false} />
</div>
<AppPagination
current={page}

View File

@ -541,7 +541,7 @@ const HotWords: React.FC = () => {
dataSource={data}
rowKey="id"
loading={loading}
scroll={{ x: "max-content" }}
scroll={{ x: "max-content", y: "100%" }}
pagination={false}
/>
</div>

View File

@ -171,7 +171,7 @@ export default function LicenseManagement() {
>
<Row gutter={24} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16 }}>
<Card variant="borderless" style={{ borderRadius: 16 }}>
<Space size={16}>
<KeyOutlined style={{ color: "#1677ff", fontSize: 24 }} />
<div>
@ -182,7 +182,7 @@ export default function LicenseManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16 }}>
<Card variant="borderless" style={{ borderRadius: 16 }}>
<Space size={16}>
<LinkOutlined style={{ color: "#52c41a", fontSize: 24 }} />
<div>
@ -193,7 +193,7 @@ export default function LicenseManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16 }}>
<Card variant="borderless" style={{ borderRadius: 16 }}>
<Space size={16}>
<CheckCircleOutlined style={{ color: "#722ed1", fontSize: 24 }} />
<div>
@ -204,7 +204,7 @@ export default function LicenseManagement() {
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16 }}>
<Card variant="borderless" style={{ borderRadius: 16 }}>
<Space size={16}>
<ClockCircleOutlined style={{ color: "#fa8c16", fontSize: 24 }} />
<div>

View File

@ -2,12 +2,33 @@
height: 100%;
display: flex;
flex-direction: column;
}
// 强制覆盖全局可能存在的 max-height: none !important 限制
// 确保 antd table 的 scroll.y 能够生效
.ant-table-wrapper .ant-table-body {
max-height: inherit !important;
}
.dictionaries-page > .ant-row {
flex: 1 1 0;
height: 0;
min-height: 0;
overflow: hidden;
align-items: stretch;
}
.dictionaries-page > .ant-row > .ant-col {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.dictionaries-page > .ant-row > .ant-col > .app-page__panel-card {
flex: 1 1 auto;
height: 100%;
min-height: 0;
overflow: hidden;
}
.dictionaries-page > .ant-row > .ant-col > .app-page__panel-card > .ant-card-body {
min-height: 0;
}
.dictionaries-header {
@ -86,6 +107,89 @@
opacity: 1;
}
.dict-type-table-wrap,
.dict-item-table-wrap {
flex: 1;
min-height: 0;
overflow: hidden;
}
.dictionaries-page .dict-type-table-wrap .ant-table-wrapper,
.dictionaries-page .dict-type-table-wrap .ant-spin-container,
.dictionaries-page .dict-type-table-wrap .ant-table,
.dictionaries-page .dict-type-table-wrap .ant-table-container,
.dictionaries-page .dict-item-table-wrap .ant-table-wrapper,
.dictionaries-page .dict-item-table-wrap .ant-spin-container,
.dictionaries-page .dict-item-table-wrap .ant-table,
.dictionaries-page .dict-item-table-wrap .ant-table-container {
height: 100%;
min-height: 0;
}
.dictionaries-page .dict-type-table-wrap .ant-table-wrapper,
.dictionaries-page .dict-item-table-wrap .ant-table-wrapper {
display: flex;
flex-direction: column;
}
.dictionaries-page .dict-type-table-wrap .ant-spin-nested-loading,
.dictionaries-page .dict-item-table-wrap .ant-spin-nested-loading {
flex: 1 1 0;
height: auto;
min-height: 0;
overflow: hidden;
}
.dictionaries-page .dict-type-table-wrap .ant-spin-container,
.dictionaries-page .dict-type-table-wrap .ant-table,
.dictionaries-page .dict-type-table-wrap .ant-table-container,
.dictionaries-page .dict-item-table-wrap .ant-spin-container,
.dictionaries-page .dict-item-table-wrap .ant-table,
.dictionaries-page .dict-item-table-wrap .ant-table-container {
display: flex;
flex-direction: column;
}
.dictionaries-page .dict-type-table-wrap .ant-table-body,
.dictionaries-page .dict-item-table-wrap .ant-table-body {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto !important;
}
.dictionaries-page .dict-type-pagination {
flex-shrink: 0;
justify-content: center;
padding: 10px 4px 0;
column-gap: 6px;
row-gap: 8px;
margin-top: 0 !important;
}
.dictionaries-page .dict-type-pagination .ant-pagination-simple-pager {
margin-inline-end: 0;
display: inline-flex;
align-items: center;
gap: 10px;
}
.dictionaries-page .dict-type-pagination .ant-pagination-simple-pager input {
margin-inline-end: 0;
font-size: 14px;
}
.dictionaries-page .dict-type-pagination .ant-pagination-simple-pager .ant-pagination-slash {
display: none;
}
.dictionaries-page .dict-type-pagination .ant-pagination-total-text {
margin-right: 0;
}
.dictionaries-page .dict-type-pagination .ant-pagination-options {
margin-inline-start: 0;
}
.tabular-nums {
font-variant-numeric: tabular-nums;
}
@ -99,9 +203,3 @@
.h-full {
height: 100%;
}
.ant-table-wrapper .ant-spin-container {
height: calc(100vh - 410px);
display: flex;
flex-direction: column;
min-height: 0;
}

View File

@ -193,7 +193,7 @@ export default function Dictionaries() {
<Button onClick={handleTypeReset}>{t("common.reset")}</Button>
</Space.Compact>
</div>
<div className="flex-1 min-h-0">
<div className="dict-type-table-wrap">
<Table
rowKey="dictTypeId"
loading={loadingTypes}
@ -202,11 +202,13 @@ export default function Dictionaries() {
...getStandardPagination(typeTotal, typeParams.current, typeParams.size, (page, size) => setTypeParams({ ...typeParams, current: page, size })),
simple: true,
size: "small",
position: ["bottomCenter"]
showQuickJumper: false,
position: ["bottomCenter"],
className: "app-global-pagination dict-type-pagination"
}}
size="small"
showHeader={false}
scroll={{ y: "calc(100vh - 480px)" }}
scroll={{ y: "100%" }}
onRow={(record) => ({ onClick: () => setSelectedType(record), className: `cursor-pointer dict-type-row ${selectedType?.dictTypeId === record.dictTypeId ? "dict-type-row-selected" : ""}` })}
columns={[
{
@ -232,7 +234,7 @@ export default function Dictionaries() {
<Col span={16} className="h-full flex flex-col overflow-hidden">
<Card title={<Space><ProfileOutlined aria-hidden="true" /><span>{t("dicts.dictItem")}{selectedType ? ` - ${selectedType.typeName}` : ""}</span></Space>} className="app-page__panel-card flex-1 flex flex-col overflow-hidden" styles={{ body: { padding: 0, flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" } }} extra={can("sys_dict:item:create") && <Button type="primary" size="small" icon={<PlusOutlined aria-hidden="true" />} onClick={handleAddItem} disabled={!selectedType}>{t("common.create")}</Button>}>
{selectedType ? (
<div className="flex-1 overflow-hidden">
<div className="dict-item-table-wrap">
<Table
rowKey="dictItemId"
loading={loadingItems}

View File

@ -12,19 +12,23 @@ export const getStandardPagination = (
overrides: TablePaginationConfig = {}
): TablePaginationConfig => {
const mergedClassName = ['app-global-pagination', overrides.className].filter(Boolean).join(' ');
const mergedShowSizeChanger =
overrides.showSizeChanger === undefined || overrides.showSizeChanger === true
? { showSearch: false }
: overrides.showSizeChanger;
return {
total,
current,
pageSize,
onChange,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (totalCount) => i18n.t('common.total', { total: totalCount }),
pageSizeOptions: ['10', '20', '50', '100'],
size: 'default',
position: ['bottomRight'],
...overrides,
showSizeChanger: mergedShowSizeChanger,
className: mergedClassName
};
};