import { Link, Outlet, useLocation, useNavigate } from '../shared/router.jsx'
import { cn } from '../lib/utils'
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarRail,
SidebarTrigger,
useSidebar,
} from '../components/ui/sidebar'
import { Separator } from '../components/ui/separator'
const menu = [
{
section: '运营',
items: [
{ to: '/accounts/import', icon: 'ri-upload-cloud-2-line', label: '账号导入' },
{ to: '/accounts', icon: 'ri-account-circle-line', label: '我的账号' },
{ to: '/accounts/monitoring', icon: 'ri-eye-line', label: '监控账号' },
{ to: '/creator/competitors', icon: 'ri-line-chart-line', label: '竞品分析' },
{ to: '/creator/workbench', icon: 'ri-chat-3-line', label: '运营工作台' },
{ to: '/creator/settings', icon: 'ri-settings-3-line', label: '采集设置' },
{ to: '/tasks', icon: 'ri-task-line', label: '任务中心' },
],
},
{
section: '资源',
items: [
{ to: '/browsers', icon: 'ri-hard-drive-3-line', label: '运行环境' },
{ to: '/network-exits', icon: 'ri-earth-line', label: '网络出口' },
{ to: '/gateways', icon: 'ri-router-line', label: '网关管理' },
],
},
]
function isActivePath(pathname, to) {
return pathname === to || pathname.startsWith(`${to}/`)
}
function MenuLink({ item, pathname }) {
const { isMobile, setOpenMobile } = useSidebar()
return (
{ if (isMobile) setOpenMobile(false) }}>
{item.label}
)
}
function AppSidebar({ pathname }) {
const navigate = useNavigate()
return (
CreatorHub
运营控制台
{menu.map((group) => (
{group.section}
{group.items.map((item) => )}
))}
{
window.localStorage.removeItem('creatorhub.auth')
navigate({ to: '/login', replace: true })
}}
>
退出登录
)
}
const pageMetadataRules = [
{ test: (path) => path === '/accounts/import', title: '账号导入', subtitle: '管理分享链接解析任务,解析成功后自动加入监控账号。' },
{ test: (path) => path === '/accounts', title: '我的账号', subtitle: '创建和管理自己维护的账号。' },
{ test: (path) => path === '/accounts/monitoring', title: '监控账号', subtitle: '管理需要持续跟踪的竞品账号及采集状态。' },
{ test: (path) => path === '/accounts/new', title: '创建社媒账号', subtitle: '创建账号后,再在编辑页配置登录身份与账号策略。' },
{ test: (path) => path.endsWith('/edit') && path.startsWith('/accounts/'), title: '编辑社媒账号', subtitle: '维护账号资料、登录核验与自动响应策略。' },
{ test: (path) => path.startsWith('/accounts/'), title: '账号详情', subtitle: '查看账号状态、登录身份与运行环境绑定。' },
{ test: (path) => path === '/creator/competitors', title: '竞品分析', subtitle: '查看竞品作品与指标,分享链接导入请前往账号导入。' },
{ test: (path) => path === '/creator/workbench', title: '运营工作台', subtitle: '评论、线索、私信与写操作均保留来源和明确结果。' },
{ test: (path) => path === '/creator/settings', title: '采集设置', subtitle: '统一配置采集窗口、指标采集和已批准的服务。' },
{ test: (path) => path === '/competitors' || path.startsWith('/competitors/'), title: '竞品分析', subtitle: '粘贴作品分享内容,自动识别作者并加入监听队列。' },
{ test: (path) => path.startsWith('/drafts/'), title: '草稿核对', subtitle: '核对版本化草稿,确认后再将操作加入队列。' },
{ test: (path) => path.startsWith('/attempts/'), title: 'Attempt 详情', subtitle: '查看执行结果、运行环境与网络出口。' },
{ test: (path) => path === '/tasks', title: '任务中心', subtitle: '逐条处理暂停、版本变化与未知执行结果。' },
{ test: (path) => path.startsWith('/tasks/'), title: '任务详情', subtitle: '查看任务状态、执行结果与允许的下一步操作。' },
{ test: (path) => path === '/browsers', title: '运行环境', subtitle: '启动、停止并回收隔离的指纹浏览器运行环境。' },
{ test: (path) => path === '/browsers/new', title: '创建运行环境', subtitle: '为已授权且暂停的账号绑定网关、指纹与网络出口。' },
{ test: (path) => path.startsWith('/browsers/'), title: '运行环境详情', subtitle: '查看环境状态、账号绑定和运行实例信息。' },
{ test: (path) => path === '/network-exits', title: '网络出口', subtitle: '维护固定出口、健康观测与账号绑定。' },
{ test: (path) => path.startsWith('/network-exits/'), title: '网络出口详情', subtitle: '查看出口配置、健康状态和绑定关系。' },
{ test: (path) => path === '/gateways', title: '网关管理', subtitle: '注册并检查宿主机 native browser gateway。' },
{ test: (path) => path.startsWith('/gateways/'), title: '网关详情', subtitle: '查看网关连接信息和运行状态。' },
]
function pageMetadata(pathname) {
const page = pageMetadataRules.find((item) => item.test(pathname))
if (page) return { title: page.title, subtitle: page.subtitle }
for (const group of menu) {
const match = group.items.find((item) => isActivePath(pathname, item.to))
if (match) return { title: match.label, subtitle: 'CreatorHub 运营控制台' }
}
return { title: 'CreatorHub', subtitle: '运营控制台' }
}
export function CreatorHubLayout({ children }) {
const { pathname } = useLocation()
const metadata = pageMetadata(pathname)
return (
跳到主要内容
{metadata.title}
{metadata.subtitle}
{children ?? }
)
}
export { menu }