fix(client): hide zero route badge (#9491)

This commit is contained in:
Zeke Zhang
2026-05-19 08:20:44 +08:00
committed by GitHub
parent ef1bb49066
commit acf5fe08b5
4 changed files with 65 additions and 8 deletions
@@ -0,0 +1,32 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { shouldDisplayRouteBadge } from '../badge';
describe('shouldDisplayRouteBadge', () => {
it('should hide zero badge by default', () => {
expect(shouldDisplayRouteBadge(0)).toBe(false);
expect(shouldDisplayRouteBadge('0')).toBe(false);
});
it('should show zero badge when showZero is enabled', () => {
expect(shouldDisplayRouteBadge(0, true)).toBe(true);
expect(shouldDisplayRouteBadge('0', true)).toBe(true);
});
it('should show non-zero badge values', () => {
expect(shouldDisplayRouteBadge(1)).toBe(true);
expect(shouldDisplayRouteBadge('待处理')).toBe(true);
});
it('should hide empty badge values', () => {
expect(shouldDisplayRouteBadge(null)).toBe(false);
expect(shouldDisplayRouteBadge(undefined)).toBe(false);
});
});
@@ -0,0 +1,18 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export type RouteBadgeCount = number | string | null | undefined;
export const isZeroRouteBadgeCount = (count: RouteBadgeCount) => {
return count === 0 || count === '0';
};
export const shouldDisplayRouteBadge = (count: RouteBadgeCount, showZero?: boolean) => {
return count != null && (!isZeroRouteBadgeCount(count) || !!showZero);
};
@@ -56,6 +56,7 @@ import { useEvaluatedExpression } from '../../../hooks/useParsedValue';
import { menuItemInitializer } from '../../../modules/menu/menuItemInitializer';
import { useMenuTranslation } from '../../../schema-component/antd/menu/locale';
import { VariableScope } from '../../../variables/VariableScope';
import { shouldDisplayRouteBadge } from './badge';
import { KeepAlive, useKeepAlive } from './KeepAlive';
import { NocoBaseDesktopRoute, NocoBaseDesktopRouteType } from './convertRoutesToSchema';
import { MenuSchemaToolbar, ResetThemeTokenAndKeepAlgorithm } from './menuItemSettings';
@@ -339,7 +340,7 @@ const GroupItem: FC<{ item: any }> = (props) => {
<SortableItem id={item._route.id} schema={fakeSchema} aria-label={item.name} style={menuItemStyle}>
{props.children}
{designable && <MenuSchemaToolbarWithContainer />}
{badgeCount != null && (
{shouldDisplayRouteBadge(badgeCount, item._route.options?.badge?.showZero) && (
<Badge
{...item._route.options.badge}
count={badgeCount}
@@ -361,9 +362,13 @@ const WithTooltip: FC<{ title: string; hidden: boolean; badgeProps: any }> = (pr
{(context) =>
context.collapsed && !props.hidden && !inHeader ? (
<Tooltip title={props.title} placement="right">
<Badge {...props.badgeProps} style={{ transform: 'none', maxWidth: '10em' }} dot={false}>
{props.children}
</Badge>
{props.badgeProps ? (
<Badge {...props.badgeProps} style={{ transform: 'none', maxWidth: '10em' }} dot={false}>
{props.children}
</Badge>
) : (
props.children
)}
</Tooltip>
) : (
props.children
@@ -384,7 +389,8 @@ const MenuItem: FC<{ item: any; options: { isMobile: boolean; collapsed: boolean
const { closeMobileMenu } = useContext(MobileMenuControlContext);
// 如果点击的是一个 group,直接跳转到第一个子页面
const path = item.redirect || item.path;
const badgeProps = { ...item._route.options?.badge, count: badgeCount };
const showBadge = shouldDisplayRouteBadge(badgeCount, item._route.options?.badge?.showZero);
const badgeProps = showBadge ? { ...item._route.options?.badge, count: badgeCount } : null;
useEffect(() => {
if (divRef.current) {
@@ -487,7 +493,7 @@ const MenuItem: FC<{ item: any; options: { isMobile: boolean; collapsed: boolean
</Link>
</div>
<MenuSchemaToolbar />
{badgeCount != null && (
{showBadge && (
<Badge
{...item._route.options?.badge}
count={badgeCount}
@@ -515,7 +521,7 @@ const MenuItem: FC<{ item: any; options: { isMobile: boolean; collapsed: boolean
</Link>
</WithTooltip>
<MenuSchemaToolbar />
{badgeCount != null && (
{showBadge && (
<Badge
{...badgeProps}
style={{ marginLeft: 4, color: item._route.options?.badge?.textColor, maxWidth: '10em' }}
@@ -45,6 +45,7 @@ import {
useCurrentRoute,
useMobileLayout,
} from '../../../route-switch/antd/admin-layout';
import { shouldDisplayRouteBadge } from '../../../route-switch/antd/admin-layout/badge';
import { NocoBaseDesktopRoute } from '../../../route-switch/antd/admin-layout/convertRoutesToSchema';
import { KeepAlive, useKeepAlive } from '../../../route-switch/antd/admin-layout/KeepAlive';
import { useGetAriaLabelOfSchemaInitializer } from '../../../schema-initializer/hooks/useGetAriaLabelOfSchemaInitializer';
@@ -270,7 +271,7 @@ const PageContent = memo((props: PageContentProps) => {
const TabBadge: FC<{ tabRoute: NocoBaseDesktopRoute; style?: React.CSSProperties }> = (props) => {
const badgeCount = useEvaluatedExpression(props.tabRoute.options?.badge?.count);
if (badgeCount == null) return null;
if (!shouldDisplayRouteBadge(badgeCount, props.tabRoute.options?.badge?.showZero)) return null;
return (
<Badge