Init Repo

This commit is contained in:
2026-02-28 03:22:04 +08:00
commit de59b57ee7
883 changed files with 156857 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
import type { RouteLocationNormalized } from 'vue-router'
import { useProjectsStore } from '@/stores/projects'
import { episodeService } from '@/services/episode'
export interface BreadcrumbItem {
label: string
href?: string
isActive?: boolean
}
export class BreadcrumbService {
static async generateBreadcrumbs(route: RouteLocationNormalized): Promise<BreadcrumbItem[]> {
const pathSegments = route.path.split('/').filter(Boolean)
const crumbs: BreadcrumbItem[] = [{ label: 'Home', href: '/' }]
// Handle different route patterns
if (pathSegments[0] === 'projects' && pathSegments[1]) {
const projectId = parseInt(pathSegments[1])
if (!isNaN(projectId)) {
// Get project information - initialize store here when needed
const projectsStore = useProjectsStore()
let project = projectsStore.getProjectById(projectId)
if (!project) {
try {
project = await projectsStore.getProject(projectId)
} catch (error) {
console.error('Failed to load project for breadcrumbs:', error)
}
}
// Add project breadcrumb
crumbs.push({
label: project ? project.name : `Project ${projectId}`,
href: `/projects/${projectId}`
})
// Handle tab-based navigation
const tab = route.meta?.tab as string
const tabLabel = route.meta?.tabLabel as string || this.getTabLabel(tab)
if (tab) {
// For shots tab with episode context
if (tab === 'shots' && route.params.episodeId) {
crumbs.push({
label: tabLabel,
href: `/projects/${projectId}/shots`
})
// Add episode context
const episodeId = parseInt(route.params.episodeId as string)
if (!isNaN(episodeId)) {
try {
const episodes = await episodeService.getProjectEpisodes(projectId)
const episode = episodes.find(ep => ep.id === episodeId)
crumbs.push({
label: episode ? episode.name : `Episode ${episodeId}`,
isActive: true
})
} catch (error) {
console.error('Failed to load episode for breadcrumbs:', error)
crumbs.push({
label: `Episode ${episodeId}`,
isActive: true
})
}
}
} else if (pathSegments[2] || tab !== 'overview') {
// Regular tab navigation (don't show Overview in breadcrumbs unless explicitly navigated to)
crumbs.push({
label: tabLabel,
isActive: true
})
}
}
}
} else {
// Handle other routes
let currentPath = ''
pathSegments.forEach((segment, index) => {
currentPath += `/${segment}`
const isLast = index === pathSegments.length - 1
// Format segment label
const label = this.formatSegmentLabel(segment)
crumbs.push({
label,
href: isLast ? undefined : currentPath,
isActive: isLast
})
})
}
return crumbs
}
private static getTabLabel(tab: string): string {
switch (tab) {
case 'overview': return 'Overview'
case 'shots': return 'Shots'
case 'assets': return 'Assets'
case 'technical-specs': return 'Technical Specs'
default: return tab.charAt(0).toUpperCase() + tab.slice(1).replace(/-/g, ' ')
}
}
private static formatSegmentLabel(segment: string): string {
// Handle special cases
if (segment === 'api-keys') return 'API Keys'
if (segment === 'technical-specs') return 'Technical Specs'
// Default formatting
return segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ')
}
}