feat(组件): 添加通用UI组件

- 添加 BrandIcon 品牌图标组件
- 添加 ThemeToggle 主题切换组件
- 添加 Toast 消息提示组件
This commit is contained in:
gaoziman 2025-12-19 22:36:42 +08:00
parent abcea67980
commit bfbeef726d
3 changed files with 126 additions and 0 deletions

View File

@ -0,0 +1,39 @@
'use client';
import { cn } from '@/lib/utils';
interface BrandIconProps {
size?: 'sm' | 'md' | 'lg' | 'xl';
className?: string;
}
const sizeMap = {
sm: 'w-6 h-6',
md: 'w-8 h-8',
lg: 'w-12 h-12',
xl: 'w-16 h-16',
};
export function BrandIcon({ size = 'md', className }: BrandIconProps) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={cn(sizeMap[size], className)}
>
{/* 八角星形图标 */}
<path
d="M12 2L14.4 8.4L21 9.6L16.2 14.4L17.4 21L12 17.6L6.6 21L7.8 14.4L3 9.6L9.6 8.4L12 2Z"
fill="currentColor"
className="text-[var(--color-primary)]"
/>
{/* 中心小星 */}
<path
d="M12 7L13 10L16 10.5L13.5 13L14 16L12 14.5L10 16L10.5 13L8 10.5L11 10L12 7Z"
fill="white"
fillOpacity="0.3"
/>
</svg>
);
}

View File

@ -0,0 +1,57 @@
'use client';
import { useState, useEffect } from 'react';
import { Moon, Sun } from 'lucide-react';
import { cn } from '@/lib/utils';
interface ThemeToggleProps {
className?: string;
}
export function ThemeToggle({ className }: ThemeToggleProps) {
const [isDark, setIsDark] = useState(false);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
// 从 localStorage 或 document 获取当前主题
const currentTheme = document.documentElement.getAttribute('data-theme');
const savedTheme = localStorage.getItem('theme');
const theme = currentTheme || savedTheme || 'light';
setIsDark(theme === 'dark');
}, []);
const toggleTheme = () => {
const newTheme = isDark ? 'light' : 'dark';
setIsDark(!isDark);
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
};
// 避免服务端渲染不匹配
if (!mounted) {
return (
<button
className={cn(
'p-2 rounded-lg text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-hover)] transition-colors',
className
)}
>
<Moon size={20} />
</button>
);
}
return (
<button
onClick={toggleTheme}
className={cn(
'p-2 rounded-lg text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-hover)] transition-colors',
className
)}
title={isDark ? '切换到浅色模式' : '切换到深色模式'}
>
{isDark ? <Sun size={20} /> : <Moon size={20} />}
</button>
);
}

View File

@ -0,0 +1,30 @@
'use client';
import { Toaster as SonnerToaster } from 'sonner';
export function Toaster() {
return (
<SonnerToaster
position="top-center"
expand={false}
richColors
closeButton
toastOptions={{
style: {
background: 'var(--color-bg-primary)',
border: '1px solid var(--color-border)',
color: 'var(--color-text-primary)',
},
classNames: {
success: 'bg-green-50 border-green-200 text-green-800 dark:bg-green-900/20 dark:border-green-800 dark:text-green-400',
error: 'bg-red-50 border-red-200 text-red-800 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400',
warning: 'bg-yellow-50 border-yellow-200 text-yellow-800 dark:bg-yellow-900/20 dark:border-yellow-800 dark:text-yellow-400',
info: 'bg-blue-50 border-blue-200 text-blue-800 dark:bg-blue-900/20 dark:border-blue-800 dark:text-blue-400',
},
}}
/>
);
}
// 导出 toast 函数以便在组件中使用
export { toast } from 'sonner';