mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-08 18:56:37 +08:00
148 lines
5.2 KiB
TypeScript
148 lines
5.2 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
|
|
import { Logo } from '@/components/icons';
|
|
import { siteConfig } from '@/config/site';
|
|
|
|
interface TabItem {
|
|
path: string;
|
|
label: string;
|
|
icon: React.ReactNode;
|
|
adminOnly?: boolean;
|
|
}
|
|
|
|
|
|
|
|
export default function H5Layout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const [isAdmin, setIsAdmin] = useState(false);
|
|
|
|
// Tabbar配置
|
|
const tabItems: TabItem[] = [
|
|
{
|
|
path: '/dashboard',
|
|
label: '首页',
|
|
icon: (
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
|
|
</svg>
|
|
)
|
|
},
|
|
{
|
|
path: '/forward',
|
|
label: '转发',
|
|
icon: (
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fillRule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clipRule="evenodd" />
|
|
</svg>
|
|
)
|
|
},
|
|
{
|
|
path: '/tunnel',
|
|
label: '隧道',
|
|
icon: (
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fillRule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clipRule="evenodd" />
|
|
</svg>
|
|
),
|
|
adminOnly: true
|
|
},
|
|
{
|
|
path: '/node',
|
|
label: '节点',
|
|
icon: (
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fillRule="evenodd" d="M3 3a1 1 0 000 2v8a2 2 0 002 2h2.586l-1.293 1.293a1 1 0 101.414 1.414L10 15.414l2.293 2.293a1 1 0 001.414-1.414L12.414 15H15a2 2 0 002-2V5a1 1 0 100-2H3zm11.707 4.707a1 1 0 00-1.414-1.414L10 9.586 8.707 8.293a1 1 0 00-1.414 0l-2 2a1 1 0 101.414 1.414L8 10.414l1.293 1.293a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
|
</svg>
|
|
),
|
|
adminOnly: true
|
|
},
|
|
{
|
|
path: '/profile',
|
|
label: '我的',
|
|
icon: (
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z" />
|
|
</svg>
|
|
)
|
|
}
|
|
];
|
|
|
|
useEffect(() => {
|
|
|
|
// 兼容处理:如果没有admin字段,根据role_id判断(0为管理员)
|
|
let adminFlag = localStorage.getItem('admin') === 'true';
|
|
if (localStorage.getItem('admin') === null) {
|
|
const roleId = parseInt(localStorage.getItem('role_id') || '1', 10);
|
|
adminFlag = roleId === 0;
|
|
// 补充设置admin字段,避免下次再次判断
|
|
localStorage.setItem('admin', adminFlag.toString());
|
|
}
|
|
|
|
|
|
setIsAdmin(adminFlag);
|
|
}, []);
|
|
|
|
// Tab点击处理
|
|
const handleTabClick = (path: string) => {
|
|
navigate(path);
|
|
};
|
|
|
|
// 过滤tab项(根据权限)
|
|
const filteredTabItems = tabItems.filter(item =>
|
|
!item.adminOnly || isAdmin
|
|
);
|
|
|
|
return (
|
|
<div className="flex flex-col h-screen bg-gray-100 dark:bg-black">
|
|
{/* 顶部导航栏 */}
|
|
<header className="bg-white dark:bg-black shadow-sm border-b border-gray-200 dark:border-gray-600 h-14 flex items-center justify-between px-4 relative z-10">
|
|
<div className="flex items-center gap-2">
|
|
<Logo size={20} />
|
|
<h1 className="text-sm font-bold text-foreground">{siteConfig.name}</h1>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
</div>
|
|
</header>
|
|
|
|
{/* 主内容区域 */}
|
|
<main className="flex-1 overflow-y-auto bg-gray-100 dark:bg-black">
|
|
{children}
|
|
</main>
|
|
|
|
{/* 底部Tabbar */}
|
|
<nav className="bg-white dark:bg-black border-t border-gray-200 dark:border-gray-600 h-16 flex items-center justify-around px-2 relative z-10">
|
|
{filteredTabItems.map((item) => {
|
|
const isActive = location.pathname === item.path;
|
|
return (
|
|
<button
|
|
key={item.path}
|
|
onClick={() => handleTabClick(item.path)}
|
|
className={`
|
|
flex flex-col items-center justify-center flex-1 h-full
|
|
transition-colors duration-200 min-h-[44px]
|
|
${isActive
|
|
? 'text-primary-600 dark:text-primary-400'
|
|
: 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200'
|
|
}
|
|
`}
|
|
>
|
|
<div className="flex-shrink-0 mb-1">
|
|
{item.icon}
|
|
</div>
|
|
<span className="text-xs font-medium">{item.label}</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
</div>
|
|
);
|
|
}
|