DHX-UI 高级组件库
DHX-UI 高级组件库
DHX Team
2024年12月8日
component-libraryreacttypescriptshadcnenterpriseui-components
基于 shadcn/ui 设计理念的企业级高级组件库,专注于提供统一API的高级组件,包括文件块、动态列表、加载项、AI浮动窗口和主题系统
DHX-UI 高级组件库
DHX-UI 是一个基于 shadcn/ui 设计理念构建的企业级高级组件库。我们专注于提供统一、易用、功能强大的高级组件,帮助开发团队快速构建复杂的企业级应用。
🚀 核心理念
设计哲学
- API 统一性: 所有组件遵循统一的 API 设计模式,降低学习成本
- 高度可定制: 基于 shadcn/ui 的设计系统,完全可定制和可扩展
- 专注高级组件: 不重复造轮子,专注于实现复杂的高级组件
- TypeScript 原生支持: 完整的类型定义,提供优秀的开发体验
- 按需导入: 支持 tree-shaking,零运行时开销
- 模块化设计: 每个组件独立,可单独使用
与 shadcn/ui 的关系
DHX-UI 并不是要替代 shadcn/ui,而是在其基础上构建更高级的组件:
shadcn/ui (基础组件)
├── Button, Input, Select 等基础组件
└── 设计系统 (Design Tokens)
DHX-UI (高级组件)
├── FileBlock (文件块)
├── DynamicList (动态列表)
├── Loading (加载项)
├── AIFloatingWindow (AI浮动窗口)
├── Theme (主题系统)
└── 基于 shadcn/ui 构建
📦 组件概览
1. FileBlock 文件块组件
企业级文件上传、预览和管理组件,支持多种文件类型和交互方式。
核心特性
- 文件上传: 支持点击上传、拖拽上传、多文件上传
- 文件预览: 支持图片、视频、PDF、文本等常见文件类型预览
- 进度显示: 实时显示上传进度和状态
- 文件管理: 文件列表展示、删除、重命名、下载
- 文件验证: 文件类型、大小、数量验证
- 缩略图生成: 自动生成文件缩略图
- 错误处理: 完善的错误提示和重试机制
API 设计
interface FileBlockProps {
// 基础配置
multiple?: boolean;
accept?: string | string[];
maxSize?: number; // 字节
maxCount?: number;
disabled?: boolean;
// 文件状态
files?: FileItem[];
defaultFiles?: FileItem[];
// 上传配置
uploadUrl?: string;
headers?: Record<string, string>;
customRequest?: (file: File) => Promise<UploadResponse>;
// 预览配置
preview?: boolean;
previewListType?: 'text' | 'picture' | 'picture-card';
// 样式配置
className?: string;
listClassName?: string;
itemClassName?: string;
// 事件处理
onChange?: (files: FileItem[]) => void;
onUpload?: (file: FileItem) => void;
onRemove?: (file: FileItem) => void;
onPreview?: (file: FileItem) => void;
onError?: (error: FileError) => void;
// 自定义渲染
renderItem?: (file: FileItem, actions: FileActions) => React.ReactNode;
renderPreview?: (file: FileItem) => React.ReactNode;
}
interface FileItem {
uid: string;
name: string;
size: number;
type: string;
url?: string;
status: 'pending' | 'uploading' | 'done' | 'error';
percent?: number;
thumbUrl?: string;
error?: string;
}
interface FileActions {
preview: () => void;
download: () => void;
remove: () => void;
retry: () => void;
}
基础用法
import { FileBlock } from 'dhx-ui/file-block';
function FileUploadExample() {
const [files, setFiles] = useState<FileItem[]>([]);
return (
<FileBlock
multiple
accept="image/*,.pdf,.doc,.docx"
maxSize={10 * 1024 * 1024} // 10MB
maxCount={5}
files={files}
onChange={setFiles}
uploadUrl="/api/upload"
preview
previewListType="picture-card"
/>
);
}
高级用法
// 自定义上传请求
function CustomUploadExample() {
const customRequest = async (file: File): Promise<UploadResponse> => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
headers: {
'Authorization': `Bearer ${token}`,
},
});
const data = await response.json();
return {
url: data.url,
thumbUrl: data.thumbUrl,
};
};
return (
<FileBlock
customRequest={customRequest}
onError={(error) => {
toast.error(`上传失败: ${error.message}`);
}}
/>
);
}
// 自定义文件项渲染
function CustomRenderExample() {
return (
<FileBlock
renderItem={(file, actions) => (
<div className="flex items-center justify-between p-2 border rounded">
<div className="flex items-center gap-2">
<FileIcon type={file.type} />
<span>{file.name}</span>
<span className="text-sm text-muted-foreground">
{(file.size / 1024).toFixed(2)} KB
</span>
</div>
<div className="flex gap-1">
<Button variant="ghost" size="sm" onClick={actions.preview}>
预览
</Button>
<Button variant="ghost" size="sm" onClick={actions.remove}>
删除
</Button>
</div>
</div>
)}
/>
);
}
2. DynamicList 动态渲染项容器
高性能的列表容器组件,支持虚拟滚动、无限滚动和动态加载。 另一种不限定方向的功能模式
核心特性
- 虚拟滚动: 支持百万级数据流畅滚动,只渲染可见区域
- 无限滚动: 自动加载更多数据,支持上拉加载和下拉刷新
- 动态加载: 支持按需加载数据,优化初始渲染性能
- 自定义渲染: 完全可定制的项渲染函数
- 滚动定位: 支持滚动到指定项、滚动到顶部/底部
- 性能优化: 智能的渲染优化和内存管理
API 设计
interface DynamicListProps<T = any> {
// 数据配置
data?: T[];
dataSource?: () => Promise<T[]>;
loadMore?: (page: number, pageSize: number) => Promise<T[]>;
// 渲染配置
renderItem: (item: T, index: number) => React.ReactNode;
itemKey?: (item: T, index: number) => string | number;
itemHeight?: number | ((item: T, index: number) => number);
// 虚拟滚动配置
virtual?: boolean;
height?: number | string;
overscan?: number; // 预渲染的项目数量
// 无限滚动配置
infiniteScroll?: boolean;
hasMore?: boolean;
pageSize?: number;
// 加载状态
loading?: boolean;
loadingComponent?: React.ReactNode;
emptyComponent?: React.ReactNode;
// 样式配置
className?: string;
itemClassName?: string;
containerClassName?: string;
// 事件处理
onScroll?: (event: React.UIEvent<HTMLDivElement>) => void;
onScrollToBottom?: () => void;
onLoadMore?: () => void;
}
interface DynamicListRef {
scrollTo: (index: number, align?: 'top' | 'center' | 'bottom') => void;
scrollToTop: () => void;
scrollToBottom: () => void;
refresh: () => void;
}
基础用法
import { DynamicList } from 'dhx-ui/dynamic-list';
interface User {
id: number;
name: string;
email: string;
}
function UserListExample() {
const users: User[] = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
// ... 更多数据
];
return (
<DynamicList
data={users}
renderItem={(user) => (
<div className="p-4 border-b">
<h3>{user.name}</h3>
<p className="text-sm text-muted-foreground">{user.email}</p>
</div>
)}
itemKey={(user) => user.id}
height={600}
virtual
/>
);
}
高级用法
// 无限滚动
function InfiniteScrollExample() {
const [data, setData] = useState<User[]>([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const loadMore = async () => {
if (loading || !hasMore) return;
setLoading(true);
const newData = await fetchUsers(page);
setData((prev) => [...prev, ...newData]);
setHasMore(newData.length === 20); // 假设每页20条
setPage((prev) => prev + 1);
setLoading(false);
};
return (
<DynamicList
data={data}
infiniteScroll
hasMore={hasMore}
loading={loading}
onLoadMore={loadMore}
renderItem={(user) => <UserItem user={user} />}
itemKey={(user) => user.id}
height="100vh"
/>
);
}
// 动态高度
function VariableHeightExample() {
return (
<DynamicList
data={posts}
itemHeight={(post) => {
// 根据内容计算高度
const baseHeight = 100;
const contentHeight = post.content.length / 50 * 20;
return baseHeight + contentHeight;
}}
renderItem={(post) => (
<div className="p-4">
<h2>{post.title}</h2>
<p>{post.content}</p>
</div>
)}
virtual
height={600}
/>
);
}
3. Loading 加载项组件
统一的加载状态组件,支持页面加载和全局加载场景。
核心特性
- PageLoading 页面加载: 全屏页面加载状态
- GlobalLoading 全局加载: 全局遮罩层加载状态
- 多种动画: 内置多种加载动画效果
- 可定制: 完全可定制的加载文案和样式
- 状态管理: 统一的加载状态管理 API
API 设计
// PageLoading 组件
interface PageLoadingProps {
loading?: boolean;
text?: string;
spinner?: 'default' | 'dots' | 'spinner' | 'orbit' | 'moon';
className?: string;
fullScreen?: boolean;
}
// GlobalLoading 组件
interface GlobalLoadingProps {
loading?: boolean;
text?: string;
spinner?: 'default' | 'dots' | 'spinner' | 'orbit' | 'moon';
zIndex?: number;
background?: string;
}
// 全局加载管理器
interface LoadingManager {
show: (options?: LoadingOptions) => () => void;
hide: (id?: string) => void;
hideAll: () => void;
}
interface LoadingOptions {
id?: string;
text?: string;
spinner?: string;
zIndex?: number;
background?: string;
}
基础用法
import { PageLoading, GlobalLoading, useLoading } from 'dhx-ui/loading';
// PageLoading 使用
function PageExample() {
const [loading, setLoading] = useState(true);
useEffect(() => {
// 模拟数据加载
fetchData().then(() => setLoading(false));
}, []);
if (loading) {
return <PageLoading text="加载中..." />;
}
return <div>页面内容</div>;
}
// GlobalLoading 使用
function ComponentExample() {
const [loading, setLoading] = useState(false);
const handleSubmit = async () => {
setLoading(true);
await submitForm();
setLoading(false);
};
return (
<>
<GlobalLoading loading={loading} text="提交中..." />
<form onSubmit={handleSubmit}>
{/* 表单内容 */}
</form>
</>
);
}
高级用法
// 使用全局加载管理器
function GlobalManagerExample() {
const loading = useLoading();
const handleOperation = async () => {
const hide = loading.show({
text: '正在处理...',
spinner: 'orbit',
});
try {
await performOperation();
} finally {
hide();
}
};
return <Button onClick={handleOperation}>执行操作</Button>;
}
// 多个加载状态管理
function MultipleLoadingExample() {
const loading = useLoading();
const handleMultipleOperations = async () => {
const hide1 = loading.show({ id: 'upload', text: '上传中...' });
await uploadFile();
hide1();
const hide2 = loading.show({ id: 'process', text: '处理中...' });
await processFile();
hide2();
};
return <Button onClick={handleMultipleOperations}>执行操作</Button>;
}
// 自定义加载动画
function CustomSpinnerExample() {
return (
<PageLoading
loading
spinner="moon"
text="自定义加载动画"
className="custom-loading"
/>
);
}
4. AIFloatingWindow AI 全局浮动窗口
AI 助手浮动窗口组件,支持圆形浮动球和展开的对话界面。
核心特性
- 圆形浮动球: 最小化状态显示为圆形浮动按钮
- 可展开窗口: 点击后展开为对话窗口
- 拖拽支持: 支持拖拽移动到任意位置
- 位置记忆: 记住窗口位置,刷新后保持
- 快捷操作: 支持快捷操作菜单
- 对话界面: 完整的对话界面,支持消息发送和接收
- 动画效果: 流畅的展开/收起动画
API 设计
interface AIFloatingWindowProps {
// 显示配置
visible?: boolean;
defaultVisible?: boolean;
defaultPosition?: { x: number; y: number };
// 样式配置
size?: 'small' | 'medium' | 'large';
theme?: 'light' | 'dark' | 'auto';
className?: string;
// 浮动球配置
icon?: React.ReactNode;
badge?: number | React.ReactNode;
tooltip?: string;
// 窗口配置
title?: string;
width?: number;
height?: number;
minWidth?: number;
minHeight?: number;
maxWidth?: number;
maxHeight?: number;
// 功能配置
draggable?: boolean;
resizable?: boolean;
rememberPosition?: boolean;
// 快捷操作
quickActions?: QuickAction[];
// 对话配置
chatConfig?: ChatConfig;
// 事件处理
onVisibleChange?: (visible: boolean) => void;
onPositionChange?: (position: { x: number; y: number }) => void;
onMessage?: (message: string) => void;
}
interface QuickAction {
id: string;
label: string;
icon?: React.ReactNode;
onClick: () => void;
}
interface ChatConfig {
placeholder?: string;
sendButtonText?: string;
onSend?: (message: string) => Promise<ChatResponse>;
renderMessage?: (message: ChatMessage) => React.ReactNode;
}
interface ChatMessage {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: Date;
}
interface ChatResponse {
message: string;
stream?: boolean;
}
基础用法
import { AIFloatingWindow } from 'dhx-ui/ai-floating-window';
function AIAssistantExample() {
const [visible, setVisible] = useState(false);
const handleSend = async (message: string) => {
// 发送消息到 AI 服务
const response = await fetch('/api/ai/chat', {
method: 'POST',
body: JSON.stringify({ message }),
});
const data = await response.json();
return { message: data.response };
};
return (
<AIFloatingWindow
visible={visible}
onVisibleChange={setVisible}
title="AI 助手"
chatConfig={{
placeholder: "输入您的问题...",
onSend: handleSend,
}}
quickActions={[
{
id: 'help',
label: '帮助',
onClick: () => console.log('帮助'),
},
{
id: 'clear',
label: '清空',
onClick: () => console.log('清空'),
},
]}
/>
);
}
高级用法
// 流式响应
function StreamingResponseExample() {
const handleSend = async (message: string) => {
const response = await fetch('/api/ai/chat/stream', {
method: 'POST',
body: JSON.stringify({ message }),
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
return {
message: '',
stream: true,
streamHandler: async (onChunk: (chunk: string) => void) => {
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const chunk = decoder.decode(value);
onChunk(chunk);
}
},
};
};
return (
<AIFloatingWindow
chatConfig={{ onSend: handleSend }}
/>
);
}
// 自定义消息渲染
function CustomMessageExample() {
return (
<AIFloatingWindow
chatConfig={{
renderMessage: (message) => (
<div className={cn(
"flex gap-2 p-3 rounded-lg",
message.role === 'user' ? "bg-primary text-primary-foreground ml-auto" : "bg-muted"
)}>
<Avatar>
{message.role === 'user' ? 'U' : 'AI'}
</Avatar>
<div>
<div className="font-medium">
{message.role === 'user' ? '您' : 'AI 助手'}
</div>
<div className="mt-1">{message.content}</div>
<div className="text-xs opacity-70 mt-1">
{formatTime(message.timestamp)}
</div>
</div>
</div>
),
}}
/>
);
}
// 位置记忆
function PositionMemoryExample() {
return (
<AIFloatingWindow
rememberPosition
defaultPosition={{ x: window.innerWidth - 400, y: 100 }}
draggable
/>
);
}
5. Theme 主题包系统
完整的主题系统,提供符合人眼阅读的饱和色彩方案。
核心特性
- 饱和色彩方案: 基于人眼阅读优化的色彩搭配
- 亮色/暗色主题: 完整的亮色和暗色主题支持
- CSS Variables: 使用 CSS 变量实现主题切换
- 设计 Tokens: 统一的颜色、间距、字体等设计令牌
- 主题定制: 支持自定义主题颜色和配置
- 主题切换: 平滑的主题切换动画
- 响应式: 支持系统主题跟随
API 设计
// 主题配置
interface ThemeConfig {
// 颜色配置
colors: {
// 主色系
primary: ColorScale;
secondary: ColorScale;
accent: ColorScale;
// 语义化颜色
success: ColorScale;
warning: ColorScale;
error: ColorScale;
info: ColorScale;
// 中性色
background: ColorScale;
foreground: ColorScale;
muted: ColorScale;
border: ColorScale;
// 文本颜色
text: {
primary: string;
secondary: string;
disabled: string;
inverse: string;
};
};
// 间距
spacing: SpacingScale;
// 字体
typography: TypographyConfig;
// 圆角
radius: RadiusScale;
// 阴影
shadows: ShadowScale;
}
interface ColorScale {
50: string;
100: string;
200: string;
300: string;
400: string;
500: string;
600: string;
700: string;
800: string;
900: string;
950: string;
}
interface SpacingScale {
xs: string;
sm: string;
md: string;
lg: string;
xl: string;
'2xl': string;
'3xl': string;
'4xl': string;
}
// ThemeProvider 组件
interface ThemeProviderProps {
theme?: 'light' | 'dark' | 'auto';
config?: Partial<ThemeConfig>;
children: React.ReactNode;
}
// 主题 Hook
interface UseThemeReturn {
theme: 'light' | 'dark';
setTheme: (theme: 'light' | 'dark' | 'auto') => void;
toggleTheme: () => void;
colors: ThemeConfig['colors'];
}
基础用法
import { ThemeProvider, useTheme } from 'dhx-ui/theme';
// 在应用根组件中使用
function App() {
return (
<ThemeProvider theme="auto">
<YourApp />
</ThemeProvider>
);
}
// 在组件中使用主题
function ThemedComponent() {
const { theme, setTheme, toggleTheme, colors } = useTheme();
return (
<div style={{ color: colors.text.primary }}>
<p>当前主题: {theme}</p>
<Button onClick={toggleTheme}>切换主题</Button>
</div>
);
}
高级用法
// 自定义主题配置
function CustomThemeExample() {
const customConfig: Partial<ThemeConfig> = {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554',
},
// ... 其他颜色配置
},
};
return (
<ThemeProvider theme="light" config={customConfig}>
<YourApp />
</ThemeProvider>
);
}
// CSS Variables 使用
function CSSVariablesExample() {
return (
<div className="themed-component">
<style jsx>{`
.themed-component {
background: var(--dhx-background);
color: var(--dhx-foreground);
border: 1px solid var(--dhx-border);
padding: var(--dhx-spacing-md);
border-radius: var(--dhx-radius-md);
}
.themed-component:hover {
background: var(--dhx-accent);
}
`}</style>
<p>使用 CSS Variables 的主题组件</p>
</div>
);
}
默认主题色彩方案
DHX-UI 提供了一套经过优化的饱和色彩方案,确保在不同场景下都有良好的可读性和视觉舒适度:
亮色主题
- 主色:蓝色系 (#3b82f6),提供专业和可信赖的感觉
- 背景:柔和的白色和浅灰色,减少视觉疲劳
- 文本:深灰色系,确保高对比度和可读性
暗色主题
- 主色:较亮的蓝色 (#60a5fa),在暗色背景下更突出
- 背景:深灰色系,减少光线刺激
- 文本:浅灰色系,保持清晰的层次感
语义化颜色
- 成功:绿色系,传达积极和完成的状态
- 警告:橙色系,吸引注意但不过于强烈
- 错误:红色系,清晰但不刺眼
- 信息:蓝色系,与主色协调
🛠️ 安装和配置
安装
npm install dhx-ui
# 或
yarn add dhx-ui
# 或
pnpm add dhx-ui
按需导入
所有组件支持按需导入,实现 tree-shaking 优化:
// 按需导入单个组件
import { FileBlock } from 'dhx-ui/file-block';
import { DynamicList } from 'dhx-ui/dynamic-list';
import { PageLoading, GlobalLoading, useLoading } from 'dhx-ui/loading';
import { AIFloatingWindow } from 'dhx-ui/ai-floating-window';
import { ThemeProvider, useTheme } from 'dhx-ui/theme';
依赖要求
- React 18.0+
- TypeScript 5.0+
- Tailwind CSS 3.0+
- 以下 shadcn/ui 组件(需要先安装):
- @radix-ui/react-*
- class-variance-authority
- clsx
- tailwind-merge
- lucide-react
基础配置
1. 安装必要的 shadcn/ui 组件
npx shadcn-ui@latest add button input label select checkbox
npx shadcn-ui@latest add table dropdown-menu badge dialog
npx shadcn-ui@latest add form textarea card tabs
npx shadcn-ui@latest add avatar scroll-area separator
2. 配置 Tailwind CSS
确保你的 tailwind.config.js 包含 dhx-ui 的样式:
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/dhx-ui/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
// dhx-ui 的主题配置会自动注入
},
},
plugins: [
require('tailwindcss-animate'),
],
};
3. 全局样式
在你的全局 CSS 文件中导入 dhx-ui 的主题变量:
@import 'dhx-ui/styles';
或者手动导入:
:root {
/* dhx-ui 会自动注入 CSS 变量 */
}
📋 最佳实践
1. 组件组合使用
DHX-UI 组件设计为可以灵活组合:
function FileManagementExample() {
const [files, setFiles] = useState<FileItem[]>([]);
const [loading, setLoading] = useState(false);
const loadingManager = useLoading();
const handleUpload = async () => {
setLoading(true);
try {
await uploadFiles(files);
loadingManager.show({ text: '上传成功' });
} catch (error) {
loadingManager.show({ text: '上传失败', spinner: 'error' });
} finally {
setLoading(false);
}
};
return (
<div className="space-y-4">
<FileBlock
files={files}
onChange={setFiles}
disabled={loading}
/>
<DynamicList
data={files}
renderItem={(file) => <FileItem file={file} />}
height={400}
virtual
/>
<GlobalLoading loading={loading} />
</div>
);
}
2. 性能优化
// 使用虚拟滚动处理大量数据
<DynamicList
data={largeDataSet}
virtual
height={600}
overscan={5}
itemHeight={80}
/>
// 使用按需加载减少初始加载时间
<DynamicList
dataSource={fetchData}
infiniteScroll
pageSize={20}
/>
// 使用 memo 优化渲染
const FileItem = React.memo(({ file }: { file: FileItem }) => {
// ...
});
3. 类型安全
充分利用 TypeScript 的类型系统:
// 定义完整的数据类型
interface CustomFileItem extends FileItem {
customField: string;
}
// 类型化的列表数据
interface User {
id: number;
name: string;
}
<DynamicList<User>
data={users}
renderItem={(user) => <UserItem user={user} />}
itemKey={(user) => user.id}
/>
🚧 开发路线图
v1.0 (当前开发中)
- FileBlock 核心功能实现
- DynamicList 虚拟滚动优化
- Loading 组件完善
- AIFloatingWindow 基础版本
- Theme 主题系统完整实现
- 完善的文档和示例
- 单元测试覆盖率 > 80%
v1.1 (计划中)
- FileBlock 高级预览功能(Office 文档、代码文件等)
- DynamicList 分组和排序功能
- AIFloatingWindow 语音输入支持
- Theme 主题定制工具
- Storybook 组件文档
- 国际化支持
v2.0 (未来)
- 更多高级组件(DataTable、AdvancedForm 等)
- 移动端适配
- 无障碍访问优化
- 性能监控和分析工具
- 组件市场
🤝 贡献指南
我们欢迎社区贡献!请查看 CONTRIBUTING.md 了解详细信息。
📄 许可证
MIT License - 查看 LICENSE 文件了解详细信息。
🙏 致谢
- shadcn/ui - 提供了优秀的设计系统基础
- react-window - 虚拟滚动的实现参考
- react-dropzone - 文件上传的灵感来源
最后更新:2024年12月