/ src / renderer / components / layouts / HeaderLayout.vue
HeaderLayout.vue
  1  <script setup lang="ts">
  2  import { watchEffect, computed, ref } from 'vue'
  3  import { useRoute, useRouter } from 'vue-router'
  4  import { useLayoutStore, getScreenFromPath } from '@/renderer/store/layout'
  5  // import { useTheme } from 'vuetify'
  6  import { useDisplay } from 'vuetify'
  7  import LocaleBtn from '@/renderer/components/common/LocaleBtn.vue'
  8  import { useRouteFeatures } from '@/renderer/composables/useRouteFeatures'
  9  import { getAppInfo } from '@/renderer/utils'
 10  // import { useMcpStore } from '@/renderer/store/mcp'
 11  import { useI18n } from 'vue-i18n'
 12  const { t } = useI18n()
 13  
 14  const { titleKey, hasComponent } = useRouteFeatures()
 15  
 16  const { xs, smAndUp } = useDisplay()
 17  
 18  // const { hasComponent } = useRouteFeatures()
 19  
 20  // const mcpStore = useMcpStore()
 21  
 22  const layoutStore = useLayoutStore()
 23  
 24  const router = useRouter()
 25  const route = useRoute()
 26  // const theme = useTheme()
 27  
 28  const handleRoute = (path: string): void => {
 29    router.push(path)
 30    return
 31  }
 32  
 33  watchEffect(() => {
 34    layoutStore.screen = getScreenFromPath(route.path)
 35  })
 36  
 37  const platform = ref('')
 38  
 39  watchEffect(() => {
 40    getAppInfo()
 41      .then((info) => {
 42        platform.value = info.platform
 43      })
 44      .catch((error) => {
 45        console.error('Failed to fetch app info:', error)
 46        platform.value = ''
 47      })
 48  })
 49  
 50  const items = computed(() => {
 51    return [
 52      { title: t('title.main'), testid: 'btn-menu-mcp', route: '/', icon: 'mdi-view-dashboard' },
 53      {
 54        title: t('title.chat'),
 55        testid: 'btn-menu-chat',
 56        route: '/chat',
 57        icon: 'mdi-comment-text-outline'
 58      },
 59      {
 60        title: t('title.agent'),
 61        testid: 'btn-menu-agent',
 62        route: '/agent',
 63        icon: 'mdi-account-multiple'
 64      },
 65      {
 66        title: t('title.setting'),
 67        testid: 'btn-menu-setting',
 68        route: '/setting',
 69        icon: 'mdi-cog-transfer-outline'
 70      }
 71    ]
 72  })
 73  </script>
 74  <template>
 75    <v-app-bar
 76      class="drag gradient-main text-white title-bar"
 77      block
 78      :order="-1"
 79      color="primary"
 80      height="36"
 81    >
 82      <v-app-bar-nav-icon
 83        class="ml-2 no-drag"
 84        density="compact"
 85        rounded="lg"
 86        :disabled="!hasComponent('sideDrawer').value"
 87        @click.stop="layoutStore.sidebar = !layoutStore.sidebar"
 88      >
 89      </v-app-bar-nav-icon>
 90      <LocaleBtn class="no-drag ml-3" data-testid="select-language" />
 91      <v-app-bar-title class="title text-uppercase text-title-medium">{{
 92        $t(titleKey.toString())
 93      }}</v-app-bar-title>
 94  
 95      <v-btn-toggle
 96        v-if="smAndUp"
 97        v-model="layoutStore.screen"
 98        class="no-drag ml-2"
 99        data-testid="main-menu"
100        mandatory
101        variant="text"
102        base-color="white"
103      >
104        <v-btn
105          v-for="(item, index) in items"
106          :key="index"
107          :data-testid="item.testid"
108          @click="handleRoute(item.route)"
109        >
110          <v-icon> {{ item.icon }} </v-icon>
111        </v-btn>
112      </v-btn-toggle>
113      <v-menu v-if="xs">
114        <template #activator="{ props }">
115          <v-btn color="white" v-bind="props" class="no-drag ml-2" icon="mdi-apps" rounded="lg">
116          </v-btn>
117        </template>
118        <v-list nav>
119          <v-list-item
120            v-for="(item, index) in items"
121            :key="index"
122            :value="index"
123            slim
124            :title="item.title"
125            @click="handleRoute(item.route)"
126          >
127            <template #prepend>
128              <v-icon :icon="item.icon" color="secondary"></v-icon>
129            </template>
130          </v-list-item>
131        </v-list>
132      </v-menu>
133  
134      <v-spacer></v-spacer>
135  
136      <template #append>
137        <v-col style="flex: 0 0 100px"></v-col>
138      </template>
139    </v-app-bar>
140  </template>
141  <style scoped>
142  .drag {
143    app-region: drag;
144  }
145  
146  .no-drag {
147    app-region: no-drag;
148  }
149  
150  .title {
151    display: block;
152  }
153  
154  @media (max-width: 600px) {
155    .title {
156      display: none;
157    }
158  }
159  
160  .title-bar {
161    padding-left: env(titlebar-area-x, 0);
162  }
163  
164  .gradient-main {
165    background: linear-gradient(to right, #01579b, #344767 calc(100% - 120px), #344767) !important;
166    background-blend-mode: normal;
167  }
168  </style>