feat: 炫酷的登录页
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Command } from 'cmdk'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Search, Monitor, Moon, Sun, Laptop, Palette } from 'lucide-react'
|
||||
import { useAppStore } from '@/store/app'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
import './cmdk.css' // We'll add some styles for cmdk
|
||||
|
||||
export default function CommandPalette() {
|
||||
const { cmdKOpen, setCmdKOpen, setThemeHue } = useAppStore()
|
||||
const menus = useAuthStore(s => s.menus)
|
||||
const navigate = useNavigate()
|
||||
const [theme, setTheme] = useState(document.documentElement.classList.contains('dark') ? 'dark' : 'light')
|
||||
|
||||
useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault()
|
||||
setCmdKOpen(!cmdKOpen)
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', down)
|
||||
return () => document.removeEventListener('keydown', down)
|
||||
}, [cmdKOpen, setCmdKOpen])
|
||||
|
||||
const runCommand = (command: () => void) => {
|
||||
setCmdKOpen(false)
|
||||
command()
|
||||
}
|
||||
|
||||
const toggleTheme = (mode: 'light' | 'dark' | 'system') => {
|
||||
if (mode === 'dark') {
|
||||
document.documentElement.classList.add('dark')
|
||||
setTheme('dark')
|
||||
} else if (mode === 'light') {
|
||||
document.documentElement.classList.remove('dark')
|
||||
setTheme('light')
|
||||
} else {
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.documentElement.classList.add('dark')
|
||||
setTheme('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
setTheme('light')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flatten menus for search
|
||||
const flatMenus: { title: string, path: string }[] = []
|
||||
const flatten = (items: any[]) => {
|
||||
items?.forEach(i => {
|
||||
if (i.path && !i.children?.length && i.path !== '/dashboard') {
|
||||
flatMenus.push({ title: i.title || i.name, path: i.path })
|
||||
}
|
||||
if (i.children) flatten(i.children)
|
||||
})
|
||||
}
|
||||
if (menus) flatten(menus)
|
||||
|
||||
return (
|
||||
<Command.Dialog open={cmdKOpen} onOpenChange={setCmdKOpen} label="Global Command Menu" className="cmdk-dialog glass-panel">
|
||||
<div className="flex items-center px-4 border-b border-white/10 dark:border-white/5">
|
||||
<Search className="w-5 h-5 text-muted-foreground mr-3" />
|
||||
<Command.Input placeholder="搜索菜单,或输入命令..." className="w-full bg-transparent border-0 h-14 text-sm outline-none focus:outline-none focus-visible:ring-0 focus-visible:outline-none placeholder:text-muted-foreground text-foreground" />
|
||||
</div>
|
||||
|
||||
<Command.List className="max-h-[300px] overflow-y-auto p-2 scrollbar-none">
|
||||
<Command.Empty className="py-6 text-center text-sm text-muted-foreground">没找到相关内容.</Command.Empty>
|
||||
|
||||
<Command.Group heading="页面导航" className="text-xs font-medium text-muted-foreground mb-1 px-2 py-1">
|
||||
<Command.Item onSelect={() => runCommand(() => navigate('/dashboard'))} className="flex items-center px-3 py-2.5 text-sm rounded-lg cursor-pointer transition-colors aria-selected:bg-primary/10 aria-selected:text-primary text-foreground mt-1">
|
||||
<Monitor className="w-4 h-4 mr-3" /> 仪表盘
|
||||
</Command.Item>
|
||||
{flatMenus.map(m => (
|
||||
<Command.Item key={m.path} onSelect={() => runCommand(() => navigate(m.path))} className="flex items-center px-3 py-2.5 text-sm rounded-lg cursor-pointer transition-colors aria-selected:bg-primary/10 aria-selected:text-primary text-foreground mt-1">
|
||||
<Search className="w-4 h-4 mr-3 opacity-50" /> {m.title}
|
||||
</Command.Item>
|
||||
))}
|
||||
</Command.Group>
|
||||
|
||||
<Command.Group heading="主题模式" className="text-xs font-medium text-muted-foreground mt-4 mb-1 px-2 py-1">
|
||||
<Command.Item onSelect={() => runCommand(() => toggleTheme('light'))} className="flex items-center px-3 py-2.5 text-sm rounded-lg cursor-pointer transition-colors aria-selected:bg-primary/10 aria-selected:text-primary text-foreground mt-1">
|
||||
<Sun className="w-4 h-4 mr-3" /> 浅色模式
|
||||
</Command.Item>
|
||||
<Command.Item onSelect={() => runCommand(() => toggleTheme('dark'))} className="flex items-center px-3 py-2.5 text-sm rounded-lg cursor-pointer transition-colors aria-selected:bg-primary/10 aria-selected:text-primary text-foreground mt-1">
|
||||
<Moon className="w-4 h-4 mr-3" /> 深色模式
|
||||
</Command.Item>
|
||||
<Command.Item onSelect={() => runCommand(() => toggleTheme('system'))} className="flex items-center px-3 py-2.5 text-sm rounded-lg cursor-pointer transition-colors aria-selected:bg-primary/10 aria-selected:text-primary text-foreground mt-1">
|
||||
<Laptop className="w-4 h-4 mr-3" /> 跟随系统
|
||||
</Command.Item>
|
||||
</Command.Group>
|
||||
|
||||
<Command.Group heading="重置主题色" className="text-xs font-medium text-muted-foreground mt-4 mb-1 px-2 py-1">
|
||||
<div className="flex gap-2 px-3 py-2 mt-1">
|
||||
<button onClick={() => runCommand(() => setThemeHue('145'))} style={{ backgroundColor: 'oklch(0.55 0.12 145)' }} className="w-6 h-6 rounded-full ring-2 ring-transparent hover:ring-white/20 transition-all"></button>
|
||||
<button onClick={() => runCommand(() => setThemeHue('240'))} style={{ backgroundColor: 'oklch(0.55 0.12 240)' }} className="w-6 h-6 rounded-full ring-2 ring-transparent hover:ring-white/20 transition-all"></button>
|
||||
<button onClick={() => runCommand(() => setThemeHue('280'))} style={{ backgroundColor: 'oklch(0.55 0.12 280)' }} className="w-6 h-6 rounded-full ring-2 ring-transparent hover:ring-white/20 transition-all"></button>
|
||||
<button onClick={() => runCommand(() => setThemeHue('330'))} style={{ backgroundColor: 'oklch(0.55 0.12 330)' }} className="w-6 h-6 rounded-full ring-2 ring-transparent hover:ring-white/20 transition-all"></button>
|
||||
<button onClick={() => runCommand(() => setThemeHue('45'))} style={{ backgroundColor: 'oklch(0.65 0.18 45)' }} className="w-6 h-6 rounded-full ring-2 ring-transparent hover:ring-white/20 transition-all"></button>
|
||||
</div>
|
||||
</Command.Group>
|
||||
</Command.List>
|
||||
</Command.Dialog>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user