2026-09-19 15:00:04 +08:00
|
|
|
<script setup>
|
|
|
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
|
import { useRoute } from 'vue-router'
|
|
|
|
|
import AppTopbar from './components/AppTopbar.vue'
|
|
|
|
|
import MobileTabbar from './components/MobileTabbar.vue'
|
|
|
|
|
import SettingDrawer from './components/SettingDrawer.vue'
|
|
|
|
|
import { useStore } from './store'
|
|
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
const { refreshAll } = useStore()
|
|
|
|
|
const settingsOpen = ref(false)
|
|
|
|
|
|
|
|
|
|
const isLogin = () => route.path === '/login'
|
|
|
|
|
const isMobile = () => window.matchMedia('(max-width: 767px)').matches
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
if (!isLogin()) refreshAll()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div class="ambient"></div>
|
|
|
|
|
|
|
|
|
|
<div v-if="!isLogin()">
|
|
|
|
|
<!-- 桌面/平板顶栏 -->
|
|
|
|
|
<AppTopbar class="desktop-topbar" @open-settings="settingsOpen = true" />
|
|
|
|
|
<!-- 移动端顶栏:☰ + 品牌 -->
|
|
|
|
|
<header class="m-topbar">
|
|
|
|
|
<div class="m-brand">
|
|
|
|
|
<div class="m-logo">📦</div>
|
|
|
|
|
<div><b>Verdaccio</b> <span>Manager</span></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="m-menu" @click="settingsOpen = true">☰</div>
|
|
|
|
|
</header>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<router-view v-slot="{ Component }">
|
2026-09-19 15:29:20 +08:00
|
|
|
<transition name="fade-slide" appear>
|
|
|
|
|
<component :is="Component" :key="route.path" />
|
2026-09-19 15:00:04 +08:00
|
|
|
</transition>
|
|
|
|
|
</router-view>
|
|
|
|
|
|
|
|
|
|
<MobileTabbar v-if="!isLogin() && isMobile()" />
|
|
|
|
|
<SettingDrawer v-model="settingsOpen" />
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.m-topbar { display: none; }
|
|
|
|
|
|
|
|
|
|
@media (max-width: 767px) {
|
|
|
|
|
.desktop-topbar { display: none; }
|
|
|
|
|
.m-topbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 14px 16px 10px;
|
|
|
|
|
background: var(--card);
|
|
|
|
|
border-bottom: 1px solid var(--line);
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
z-index: 100;
|
|
|
|
|
}
|
|
|
|
|
.m-brand { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; }
|
|
|
|
|
.m-logo {
|
|
|
|
|
width: 30px;
|
|
|
|
|
height: 30px;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
display: grid;
|
|
|
|
|
place-items: center;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
background: var(--grad-main);
|
|
|
|
|
}
|
|
|
|
|
.m-brand b { color: var(--orange); }
|
|
|
|
|
.m-brand span { color: var(--ink-faint); font-weight: 600; font-size: 12px; }
|
|
|
|
|
.m-menu {
|
|
|
|
|
width: 34px;
|
|
|
|
|
height: 34px;
|
|
|
|
|
border-radius: 11px;
|
|
|
|
|
display: grid;
|
|
|
|
|
place-items: center;
|
|
|
|
|
background: var(--card-2);
|
|
|
|
|
border: 1px solid var(--line);
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: var(--ink-dim);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|