import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AuthCreditService } from '../../services/auth-credit.service'; /** * 全局侧栏 — 3 大分区纯字排版 * * 01 / 创作 生成相关(pipeline hub + 6 个生成模式) * 02 / 资产 任务 / 历史 / 结果 / 视频库 * 03 / 发现 视频发现 / 博主监测 / 语音合成 * * 设计立场:纯字排版,零图标,分区编号用 Fraunces 衬线斜体小号字, * 二级条目仅文本 + 左侧 2px 高亮竖线(active 时出现),与传统 SaaS 图标列表拉开差距。 */ export interface SidebarItem { tab: string; label: string; badge?: string; } export interface SidebarZone { key: string; serial: string; label: string; items: SidebarItem[]; } const ZONES: SidebarZone[] = [ { key: 'create', serial: '01', label: '创作', items: [ { tab: 'home', label: '首页' }, { tab: 'pipelines', label: '生成模式中心' }, { tab: 'batch-production', label: '批量生产' }, { tab: 'image-generation', label: '图片生成' }, { tab: 'topic-to-video', label: '主题生视频' }, { tab: 'image-to-video', label: '图生视频' }, { tab: 'digital-human', label: '数字人合成' }, { tab: 'action-transfer', label: '动作迁移' }, { tab: 'asset-remix', label: '素材合成视频' }, { tab: 'video-generation', label: '标准视频生成' }, ], }, { key: 'assets', serial: '02', label: '资产', items: [ { tab: 'drafts', label: '我的创作' }, { tab: 'library', label: '素材库' }, { tab: 'templates', label: '模板库' }, // 老入口保留作为兼容(后续版本可移除) { tab: 'videos', label: '视频管理' }, { tab: 'tasks', label: '任务管理' }, ], }, { key: 'discover', serial: '03', label: '发现', items: [ { tab: 'search', label: '视频发现' }, { tab: 'monitor', label: '博主监测' }, { tab: 'topic-pool', label: '选题池' }, { tab: 'retrospective', label: '发布复盘' }, { tab: 'analysis-history', label: '分析历史' }, { tab: 'daily-reports', label: '日报历史' }, { tab: 'voice-synthesis', label: '语音合成' }, ], }, { key: 'account', serial: '04', label: '账号', items: [ { tab: 'user-center', label: '用户中心' }, { tab: 'admin-users', label: '账号明细' }, ], }, ]; @Component({ selector: 'app-sidebar', standalone: true, imports: [CommonModule], templateUrl: './app-sidebar.component.html', styleUrls: ['./app-sidebar.component.css'], }) export class AppSidebarComponent { @Input() currentTab: string = ''; @Output() tabChange = new EventEmitter(); private readonly auth = inject(AuthCreditService); get zones(): SidebarZone[] { if (this.auth.isAdmin) return ZONES; return ZONES .map((zone) => ({ ...zone, items: zone.items.filter((item) => item.tab !== 'admin-users'), })) .filter((zone) => zone.items.length > 0); } /** trackBy 减少二级渲染时 DOM 重建 */ trackByZone = (_: number, z: SidebarZone) => z.key; trackByItem = (_: number, it: SidebarItem) => it.tab; isActive(tab: string): boolean { return this.currentTab === tab; } /** 鼓励通过 keyboard 操作 */ onItemKey(ev: KeyboardEvent, tab: string): void { if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); this.tabChange.emit(tab); } } }