初始化

初始化项目
This commit is contained in:
truewhile
2026-08-23 22:12:32 +08:00
parent 0bcb1fec87
commit 71bf60c69c
631 changed files with 2121 additions and 76050 deletions
+29 -108
View File
@@ -1,44 +1,29 @@
import { Link } from 'react-router-dom'
import { motion } from 'framer-motion'
import { LogOut, Menu, X } from 'lucide-react'
import { ArrowLeft, Menu, X } from 'lucide-react'
import clsx from 'clsx'
import { LAYOUT_NAV_GROUPS, NAV_GROUP_PATHS, type LayoutNavGroup, type LayoutNavItem } from './layoutNavigation'
import { SidebarGroup, SidebarLink } from './LayoutSidebarNav'
import { LAYOUT_NAV_ITEMS, type LayoutNavItem } from './layoutNavigation'
import { SidebarLink } from './LayoutSidebarNav'
export type LayoutSidebarContentProps = {
isSidebarOpen: boolean
isMobileDrawerOpen: boolean
openGroups: Record<string, boolean>
isAdmin: boolean
username?: string
can: (key: string) => boolean
isRouteIn: (paths: string[]) => boolean
onToggleGroup: (id: string) => void
onToggleSidebar: () => void
onCloseMobileDrawer: () => void
onLogout: () => void
}
type VisibleLayoutNavGroup = {
group: LayoutNavGroup
items: LayoutNavItem[]
}
export function LayoutSidebarContent({
isSidebarOpen,
isMobileDrawerOpen,
openGroups,
isAdmin,
username,
can,
isRouteIn,
onToggleGroup,
onToggleSidebar,
onCloseMobileDrawer,
onLogout,
}: LayoutSidebarContentProps) {
const sidebarExpanded = isSidebarOpen || isMobileDrawerOpen
const visibleGroups = visibleSidebarGroups({ isAdmin, can })
const visibleItems = visibleSidebarItems({ isAdmin, can })
return (
<div className="flex h-full flex-col border-r border-[var(--app-border)] bg-[var(--app-panel)]">
@@ -47,28 +32,19 @@ export function LayoutSidebarContent({
onToggleSidebar={onToggleSidebar}
onCloseMobileDrawer={onCloseMobileDrawer}
/>
<LayoutSidebarNav
groups={visibleGroups}
sidebarExpanded={sidebarExpanded}
openGroups={openGroups}
isRouteIn={isRouteIn}
onToggleGroup={onToggleGroup}
/>
<LayoutSidebarLogout sidebarExpanded={sidebarExpanded} username={username} onLogout={onLogout} />
<LayoutSidebarNav items={visibleItems} sidebarExpanded={sidebarExpanded} />
<LayoutSidebarHomeBack sidebarExpanded={sidebarExpanded} />
</div>
)
}
function visibleSidebarGroups({
function visibleSidebarItems({
isAdmin,
can,
}: Pick<LayoutSidebarContentProps, 'isAdmin' | 'can'>): VisibleLayoutNavGroup[] {
const isItemVisible = (item: LayoutNavItem) =>
(!item.adminOnly || isAdmin) && (!item.permission || can(item.permission))
return LAYOUT_NAV_GROUPS
.filter((group) => !group.adminOnly || isAdmin)
.map((group) => ({ group, items: group.items.filter(isItemVisible) }))
.filter(({ items }) => items.length > 0)
}: Pick<LayoutSidebarContentProps, 'isAdmin' | 'can'>): LayoutNavItem[] {
return LAYOUT_NAV_ITEMS.filter(
(item) => (!item.adminOnly || isAdmin) && (!item.permission || can(item.permission)),
)
}
function LayoutSidebarHeader({
@@ -84,8 +60,8 @@ function LayoutSidebarHeader({
<div className="flex h-20 items-center justify-between border-b border-[var(--app-border)] px-6">
<Link to="/" className="flex items-center gap-3">
<img
src="/brand/mediastationgo-logo.svg"
alt="MediaStationGo"
src="/brand/logo-192.png"
alt="MMTL"
className="h-10 w-10 shrink-0 rounded-xl object-contain shadow-sm"
/>
{sidebarExpanded && (
@@ -94,7 +70,7 @@ function LayoutSidebarHeader({
animate={{ opacity: 1, x: 0 }}
className="font-display text-lg font-extrabold tracking-tight text-[var(--app-text)]"
>
MediaStationGo
MMTL
</motion.span>
)}
</Link>
@@ -109,102 +85,47 @@ function LayoutSidebarHeader({
}
function LayoutSidebarNav({
groups,
sidebarExpanded,
openGroups,
isRouteIn,
onToggleGroup,
}: {
groups: VisibleLayoutNavGroup[]
sidebarExpanded: boolean
openGroups: Record<string, boolean>
isRouteIn: (paths: string[]) => boolean
onToggleGroup: (id: string) => void
}) {
return (
<nav className="flex-1 overflow-y-auto px-4 py-5 space-y-2 scrollbar-hide">
{groups.map(({ group, items }) => (
<LayoutSidebarNavGroup
key={group.id}
group={group}
items={items}
sidebarExpanded={sidebarExpanded}
open={openGroups[group.id] ?? group.id === 'media'}
active={isRouteIn(NAV_GROUP_PATHS[group.id])}
onToggleGroup={onToggleGroup}
/>
))}
</nav>
)
}
function LayoutSidebarNavGroup({
group,
items,
sidebarExpanded,
open,
active,
onToggleGroup,
}: {
group: LayoutNavGroup
items: LayoutNavItem[]
sidebarExpanded: boolean
open: boolean
active: boolean
onToggleGroup: (id: string) => void
}) {
const GroupIcon = group.icon
return (
<SidebarGroup
id={group.id}
icon={<GroupIcon size={18} />}
label={group.label}
collapsed={!sidebarExpanded}
open={open}
active={active}
onToggle={onToggleGroup}
>
<nav className="flex-1 overflow-y-auto px-4 py-5 space-y-1 scrollbar-hide">
{items.map((item) => {
const ItemIcon = item.icon
return (
<SidebarLink
key={item.to}
to={item.to}
icon={<ItemIcon size={16} />}
icon={<ItemIcon size={18} />}
label={item.label}
end={item.end}
child
collapsed={!sidebarExpanded}
/>
)
})}
</SidebarGroup>
</nav>
)
}
function LayoutSidebarLogout({
sidebarExpanded,
username,
onLogout,
}: {
sidebarExpanded: boolean
username?: string
onLogout: () => void
}) {
function LayoutSidebarHomeBack({ sidebarExpanded }: { sidebarExpanded: boolean }) {
return (
<div className="border-t border-[var(--app-border)] bg-[var(--app-panel-soft)] p-4">
<button
onClick={onLogout}
<Link
to="/"
className={clsx(
'flex items-center gap-3.5 rounded-xl px-4 py-3 text-sm font-semibold transition-all duration-300 w-full group/logout',
'flex items-center gap-3.5 rounded-xl px-4 py-3 text-sm font-semibold transition-all duration-300 w-full group',
sidebarExpanded
? 'justify-start text-[var(--app-muted)] hover:bg-[var(--app-danger-soft)] hover:text-red-500'
: 'justify-center text-[var(--app-muted)] hover:text-red-500',
? 'justify-start text-[var(--app-muted)] hover:bg-[var(--app-hover)] hover:text-[var(--app-text)]'
: 'justify-center text-[var(--app-muted)] hover:text-[var(--app-text)]',
)}
title={`安全登出 (${username ?? ''})`}
title="返回系统首页"
>
<LogOut size={18} className="transition-transform group-hover/logout:-translate-x-0.5" />
{sidebarExpanded && <span>安全退出</span>}
</button>
<ArrowLeft size={18} className="transition-transform group-hover:-translate-x-0.5" />
{sidebarExpanded && <span>返回系统首页</span>}
</Link>
</div>
)
}
@@ -229,4 +150,4 @@ function SidebarIconButton({
{children}
</button>
)
}
}