Files
soul-yongping/soul-admin/src/layouts/AdminLayout.tsx
Alex-larget c936371165 Update mini program development documentation and enhance user interface elements
- Added a new entry for the latest mini program development rules and APIs in the evolution index.
- Updated the skill documentation to include guidelines on privacy authorization and capability detection.
- Modified the read and settings pages to improve user experience with new input styles and layout adjustments.
- Implemented user-select functionality for text elements in the read page to enhance interactivity.
- Refined CSS styles for better responsiveness and visual consistency across various components.
2026-03-14 16:23:01 +08:00

139 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react'
import { Outlet, Link, useLocation, useNavigate } from 'react-router-dom'
import {
LayoutDashboard,
Users,
Settings,
LogOut,
Wallet,
BookOpen,
GitMerge,
} from 'lucide-react'
import { get, post } from '@/api/client'
import { clearAdminToken } from '@/api/auth'
import { RechargeAlert } from '@/components/RechargeAlert'
// 主菜单5 项平铺,按 Mycontent-temp 新规范)
const primaryMenuItems = [
{ icon: LayoutDashboard, label: '数据概览', href: '/dashboard' },
{ icon: BookOpen, label: '内容管理', href: '/content' },
{ icon: Users, label: '用户管理', href: '/users' },
{ icon: GitMerge, label: '找伙伴', href: '/find-partner' },
{ icon: Wallet, label: '推广中心', href: '/distribution' },
]
export function AdminLayout() {
const location = useLocation()
const navigate = useNavigate()
const [mounted, setMounted] = useState(false)
const [authChecked, setAuthChecked] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
useEffect(() => {
if (!mounted) return
setAuthChecked(false)
let cancelled = false
get<{ success?: boolean }>('/api/admin')
.then((data) => {
if (cancelled) return
if (data && (data as { success?: boolean }).success !== false) {
setAuthChecked(true)
} else {
navigate('/login', { replace: true })
}
})
.catch(() => {
if (!cancelled) navigate('/login', { replace: true })
})
return () => {
cancelled = true
}
}, [mounted, navigate])
const handleLogout = async () => {
clearAdminToken()
try {
await post('/api/admin/logout', {})
} catch {
// 忽略登出接口失败,本地已清 token
}
navigate('/login', { replace: true })
}
if (!mounted || !authChecked) {
return (
<div className="flex min-h-screen bg-[#0a1628]">
<div className="w-64 bg-[#0f2137] border-r border-gray-700/50" />
<div className="flex-1 flex items-center justify-center">
<div className="text-[#38bdac]">...</div>
</div>
</div>
)
}
return (
<div className="flex min-h-screen bg-[#0a1628]">
<div className="w-64 bg-[#0f2137] flex flex-col border-r border-gray-700/50 shadow-xl">
<div className="p-6 border-b border-gray-700/50">
<h1 className="text-xl font-bold text-[#38bdac]"></h1>
<p className="text-xs text-gray-400 mt-1">Soul创业派对</p>
</div>
<nav className="flex-1 p-4 space-y-1 overflow-y-auto">
{primaryMenuItems.map((item) => {
const isActive = location.pathname === item.href
return (
<Link
key={item.href}
to={item.href}
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${
isActive
? 'bg-[#38bdac]/20 text-[#38bdac] font-medium'
: 'text-gray-400 hover:bg-gray-700/50 hover:text-white'
}`}
>
<item.icon className="w-5 h-5 shrink-0" />
<span className="text-sm">{item.label}</span>
</Link>
)
})}
<div className="pt-4 mt-4 border-t border-gray-700/50">
<Link
to="/settings"
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${
location.pathname === '/settings'
? 'bg-[#38bdac]/20 text-[#38bdac] font-medium'
: 'text-gray-400 hover:bg-gray-700/50 hover:text-white'
}`}
>
<Settings className="w-5 h-5 shrink-0" />
<span className="text-sm"></span>
</Link>
</div>
</nav>
<div className="p-4 border-t border-gray-700/50 space-y-1">
<button
type="button"
onClick={handleLogout}
className="w-full flex items-center gap-3 px-4 py-3 text-gray-400 hover:text-white rounded-lg hover:bg-gray-700/50 transition-colors"
>
<LogOut className="w-5 h-5" />
<span className="text-sm">退</span>
</button>
</div>
</div>
<div className="flex-1 overflow-auto bg-[#0a1628] min-w-0 flex flex-col">
<RechargeAlert />
<div className="w-full min-w-[1024px] min-h-full flex-1">
<Outlet />
</div>
</div>
</div>
)
}