feat: 添加 EmptyPlaceholder 组件并更新相关页面
- 新增 EmptyPlaceholder 组件,用于统一处理空状态提示,提升用户体验。 - 更新多个页面(JsonTools、Jwt、QrCode、Timestamp)以使用 EmptyPlaceholder 组件,简化空状态的实现。 - 更新 README.md,添加 EmptyPlaceholder 组件的说明。
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface EmptyPlaceholderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children: React.ReactNode;
|
||||
messageClassName?: string;
|
||||
}
|
||||
|
||||
const CONTAINER_CLASSES =
|
||||
'rounded-xl bg-muted/30 border border-dashed border-border/80 text-center flex flex-col items-center justify-center select-none p-8 min-h-[120px]';
|
||||
|
||||
const MESSAGE_CLASSES =
|
||||
'text-xs font-semibold text-muted-foreground/80 tracking-wide max-w-[240px] leading-relaxed';
|
||||
|
||||
export default function EmptyPlaceholder({
|
||||
children,
|
||||
className,
|
||||
messageClassName,
|
||||
...props
|
||||
}: EmptyPlaceholderProps) {
|
||||
return (
|
||||
<div className={cn(CONTAINER_CLASSES, className)} {...props}>
|
||||
{typeof children === 'string' || typeof children === 'number' ? (
|
||||
<p className={cn(MESSAGE_CLASSES, messageClassName)}>{children}</p>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
| ----------------------- | ------------------------------------------------------------------------------ |
|
||||
| `RouterContainer.tsx` | 路由容器,根据当前路由动态渲染对应页面组件,集成错误边界和骨架屏 |
|
||||
| `SwitchButtonGroup.tsx` | 通用切换按钮组,支持 `small/medium/large` 三种尺寸,用于页面子模式切换 |
|
||||
| `EmptyPlaceholder.tsx` | 虚线边框空状态占位,统一工具页「暂无结果」提示样式 |
|
||||
| `TextInputArea.tsx` | 增强文本输入区域,支持校验规则、工具栏操作、字符计数、清空 |
|
||||
| `CopyButton.tsx` | 一键复制按钮,支持复制成功状态动画,封装 `copyTextToClipboard` 和 `toast` 反馈 |
|
||||
| `ImageUploader.tsx` | 图片上传组件,支持拖拽上传、文件选择和预览 |
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import EmptyPlaceholder from '@/components/EmptyPlaceholder';
|
||||
|
||||
describe('EmptyPlaceholder 组件', () => {
|
||||
it('应渲染字符串提示文本', () => {
|
||||
render(<EmptyPlaceholder>{'请输入内容'}</EmptyPlaceholder>);
|
||||
|
||||
expect(screen.getByText('请输入内容')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('应应用规范空状态容器样式', () => {
|
||||
const { container } = render(<EmptyPlaceholder>{'提示'}</EmptyPlaceholder>);
|
||||
|
||||
const placeholder = container.firstChild;
|
||||
expect(placeholder).toHaveClass(
|
||||
'rounded-xl',
|
||||
'bg-muted/30',
|
||||
'border-dashed',
|
||||
'border-border/80',
|
||||
'min-h-[120px]',
|
||||
);
|
||||
});
|
||||
|
||||
it('应支持 className 自定义容器样式', () => {
|
||||
const { container } = render(
|
||||
<EmptyPlaceholder className="flex-1 min-h-[320px]">{'提示'}</EmptyPlaceholder>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveClass('flex-1', 'min-h-[320px]');
|
||||
});
|
||||
|
||||
it('应支持 messageClassName 自定义文本样式', () => {
|
||||
render(<EmptyPlaceholder messageClassName="text-sm max-w-none">{'提示'}</EmptyPlaceholder>);
|
||||
|
||||
expect(screen.getByText('提示')).toHaveClass('text-sm', 'max-w-none');
|
||||
});
|
||||
|
||||
it('应支持 ReactNode 类型的 children', () => {
|
||||
render(
|
||||
<EmptyPlaceholder>
|
||||
<span data-testid="custom-content">自定义内容</span>
|
||||
</EmptyPlaceholder>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('custom-content')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user