AppNav.vue 8.24 KB
<template>
  <nav class="app-nav">
    <div class="nav-brand" @click="$router.push('/')">
      <span class="nav-logo">🧬</span>
      <span class="nav-title">LemonBoss</span>
    </div>
    <div class="nav-links">
      <a href="/agent/master" target="_blank" class="nav-link" :class="{ active: $route.path.startsWith('/agent') }">
        <span>🤖</span> System Agents
      </a>

      <a href="/templates" target="_blank" class="nav-link" :class="{ active: $route.path.startsWith('/templates') }">
        <span>📦</span> Templates
      </a>

      <a href="/tasks" target="_blank" class="nav-link" :class="{ active: $route.path.startsWith('/tasks') }">
        <span>📜</span> Task Logs
      </a>

      <a href="/quota" target="_blank" class="nav-link" :class="{ active: $route.path === '/quota' }">
        <span>📊</span> Quota
      </a>

      <div class="nav-divider"></div>

      <!-- Company Switcher -->
      <div class="nav-switcher-wrapper" ref="switcherRef">
        <div class="nav-switcher" @click.stop="showDropdown = !showDropdown">
          <span class="switcher-icon">🏢</span>
          <span class="switcher-label">{{ currentCompany ? currentCompany.name : 'Companies' }}</span>
          <span class="switcher-arrow">▼</span>
        </div>
        <div class="nav-dropdown" v-if="showDropdown">
          <div class="nav-dropdown-scroll">
            <div 
              v-for="c in companies" :key="c.id" 
              class="nav-dropdown-item" :class="{ active: currentCompany?.id === c.id }"
              @click="switchCompany(c.id)">
              {{ c.name }}
            </div>
            <div v-if="companies.length === 0" class="nav-dropdown-item text-muted">暂无公司</div>
          </div>
          <div class="nav-dropdown-divider"></div>
          <div class="nav-dropdown-item create-action" style="cursor:default; border:none; opacity: 0.6;">
             请唤醒 MasterBot 自动创建
          </div>
        </div>
      </div>

      <!-- Contextual Company Navigation -->
      <template v-if="currentCompany">
        <a :href="`/${currentCompany.id}/overview`" target="_blank" class="nav-link" :class="{ active: $route.params.tab === 'overview' }">
          📊 Overview
        </a>
        <a :href="`/${currentCompany.id}/agents`" target="_blank" class="nav-link" :class="{ active: $route.params.tab === 'agents' }">
          👥 Team
        </a>
        <a :href="`/${currentCompany.id}/workspace`" target="_blank" class="nav-link" :class="{ active: $route.params.tab === 'workspace' }">
          💼 Workspace
        </a>
        <a :href="`/${currentCompany.id}/pipelines`" target="_blank" class="nav-link" :class="{ active: $route.params.tab === 'pipelines' || $route.params.tab === 'sop' }">
          ⚙️ SOP
        </a>
        <a :href="`/${currentCompany.id}/goals`" target="_blank" class="nav-link" :class="{ active: $route.params.tab === 'goals' || $route.params.tab === 'okr' }">
          🎯 OKR
        </a>
        <a :href="`/${currentCompany.id}/files`" target="_blank" class="nav-link" :class="{ active: $route.params.tab === 'files' }">
          📁 Files
        </a>
        <a :href="`/${currentCompany.id}/messages`" target="_blank" class="nav-link" :class="{ active: $route.params.tab === 'messages' }">
          💬 Messages
        </a>
      </template>
    </div>
    
    <div class="nav-right">
      <button class="theme-btn" @click="cycleTheme" :title="`主题: ${themeLabel}`">
        {{ themeIcon }}
      </button>
      <span class="nav-version">v5.0</span>
    </div>
  </nav>
</template>

<script setup>
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useTheme } from '@/composables/useTheme'
import { useCompanies } from '@/composables/useCompanies'

const route = useRoute()
const router = useRouter()
const { themeMode, cycleTheme } = useTheme()
const { companies, loadCompanies, activeCompanyId, setActiveCompany } = useCompanies()

const showDropdown = ref(false)

const themeIcon = computed(() => {
  if (themeMode.value === 'light') return '☀️'
  if (themeMode.value === 'dark') return '🌙'
  return '🌗'
})
const themeLabel = computed(() => {
  if (themeMode.value === 'light') return '浅色'
  if (themeMode.value === 'dark') return '深色'
  return '跟随系统'
})

const currentCompany = computed(() => {
  const cid = route.params.id || activeCompanyId.value
  if (!cid) return null
  return companies.value.find(c => c.id === cid) || companies.value[0] || null
})

watch(() => route.params.id, (newId) => {
  if (newId) setActiveCompany(newId)
}, { immediate: true })

function switchCompany(id) {
  showDropdown.value = false
  setActiveCompany(id)
  const activeTab = route.params.tab || 'agents'
  router.push(`/${id}/${activeTab}`)
}

function closeDropdown(e) {
  showDropdown.value = false
}

onMounted(async () => {
  await loadCompanies()
  document.addEventListener('click', closeDropdown)
})

onUnmounted(() => {
  document.removeEventListener('click', closeDropdown)
})
</script>

<style scoped>
.app-nav {
  display: flex; align-items: center; height: 44px; padding: 0 16px;
  background: var(--bg-secondary); backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--border-primary);
  position: sticky; top: 0; z-index: 100; gap: 20px;
  transition: background 0.3s;
}
.nav-brand { display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; }
.nav-logo { font-size: 16px; }
.nav-title {
  font-size: 14px; font-weight: 800;
  background: linear-gradient(135deg, var(--accent), var(--accent-light));
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.nav-links { display: flex; align-items: center; gap: 6px; flex: 1; }
.nav-divider { width: 1px; height: 20px; background: var(--border-primary); margin: 0 8px; }

.nav-link {
  display: flex; align-items: center; gap: 6px; padding: 6px 12px;
  border-radius: 8px; text-decoration: none; color: var(--text-tertiary);
  font-size: 13px; font-weight: 600; transition: all 0.2s;
}
.nav-link:hover { background: var(--bg-surface-hover); color: var(--text-secondary); }
.nav-link.active { background: var(--bg-surface); color: var(--accent); box-shadow: 0 2px 8px rgba(0,0,0,0.05); }

/* Switcher */
.nav-switcher-wrapper { position: relative; margin-right: 8px; }
.nav-switcher {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 8px; cursor: pointer;
  background: var(--bg-surface); border: 1px solid var(--border-primary);
  transition: all 0.2s; font-size: 13px; font-weight: 700; color: var(--text-primary);
}
.nav-switcher:hover { background: var(--bg-surface-hover); border-color: var(--border-active); }
.switcher-arrow { font-size: 9px; color: var(--text-muted); padding-top:2px; }

.nav-dropdown {
  position: absolute; top: calc(100% + 6px); left: 0; width: 200px;
  background: var(--bg-secondary); border: 1px solid var(--border-primary);
  border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 1000;
}
.nav-dropdown-scroll { max-height: 200px; overflow-y: auto; padding: 6px; }
.nav-dropdown-item {
  padding: 8px 12px; font-size: 13px; font-weight: 600; color: var(--text-secondary);
  border-radius: 6px; cursor: pointer; transition: all 0.1s;
}
.nav-dropdown-item:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.nav-dropdown-item.active { background: var(--accent-bg); color: var(--accent-light); }
.nav-dropdown-divider { height: 1px; background: var(--border-primary); margin: 0; }
.create-action { color: var(--accent-light); text-align: center; margin: 6px; border: 1px dashed var(--border-active); }
.create-action:hover { background: var(--accent-bg); color: var(--accent-light); }

.nav-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.theme-btn {
  width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--border-primary);
  background: var(--bg-surface); cursor: pointer; font-size: 14px;
  display: flex; align-items: center; justify-content: center; transition: all 0.2s;
}
.theme-btn:hover { background: var(--bg-surface-hover); transform: scale(1.1); }
.nav-version { font-size: 10px; color: var(--text-faint); padding: 2px 6px; border: 1px solid var(--border-primary); border-radius: 4px; font-weight:600;}
</style>