- Updated docker-compose.dev.yml to use Dockerfile.dev for backend builds and added HOST environment variable for frontend. - Introduced Dockerfile.dev for streamlined backend development with Go. - Enhanced development documentation to reflect changes in local setup and API proxying. - Removed outdated frontend Dockerfile and adjusted frontend configuration for improved development experience.
129 lines
3.6 KiB
Vue
129 lines
3.6 KiB
Vue
<template>
|
|
<div class="h-screen flex flex-col bg-base-100 overflow-hidden">
|
|
<header class="navbar bg-base-100/80 backdrop-blur-xl border-b border-base-300/50 px-4 gap-2 shrink-0">
|
|
<NuxtLink to="/" class="text-lg font-semibold hover:opacity-80 transition-all duration-200">{{ title }}</NuxtLink>
|
|
<div class="flex-1" />
|
|
<NuxtLink
|
|
v-if="!isLogin"
|
|
to="/"
|
|
class="btn btn-ghost btn-sm gap-1.5 transition-all duration-200 hover:scale-105"
|
|
:class="route.path === '/' ? 'btn-primary' : ''"
|
|
>
|
|
<icons-icon-map />
|
|
Map
|
|
</NuxtLink>
|
|
<NuxtLink
|
|
v-if="!isLogin"
|
|
to="/profile"
|
|
class="btn btn-ghost btn-sm gap-1.5 transition-all duration-200 hover:scale-105"
|
|
:class="route.path === '/profile' ? 'btn-primary' : ''"
|
|
>
|
|
<icons-icon-user />
|
|
Profile
|
|
</NuxtLink>
|
|
<NuxtLink
|
|
v-if="!isLogin && isAdmin"
|
|
to="/admin"
|
|
class="btn btn-ghost btn-sm gap-1.5 transition-all duration-200 hover:scale-105"
|
|
:class="route.path.startsWith('/admin') ? 'btn-primary' : ''"
|
|
>
|
|
<icons-icon-shield />
|
|
Admin
|
|
</NuxtLink>
|
|
<button
|
|
v-if="!isLogin && me"
|
|
type="button"
|
|
class="btn btn-ghost btn-sm btn-error btn-outline gap-1.5 transition-all duration-200 hover:scale-105"
|
|
@click="doLogout"
|
|
>
|
|
<icons-icon-logout />
|
|
Logout
|
|
</button>
|
|
<label class="swap swap-rotate btn btn-ghost btn-sm transition-all duration-200 hover:scale-105">
|
|
<input type="checkbox" v-model="dark" @change="toggleTheme" />
|
|
<span class="swap-off"><icons-icon-sun /></span>
|
|
<span class="swap-on"><icons-icon-moon /></span>
|
|
</label>
|
|
<span v-if="live" class="badge badge-success badge-sm">Live</span>
|
|
</header>
|
|
<main class="flex-1 min-h-0 overflow-y-auto relative">
|
|
<slot />
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const THEME_KEY = 'hnh-map-theme'
|
|
|
|
function getInitialDark(): boolean {
|
|
if (import.meta.client) {
|
|
const stored = localStorage.getItem(THEME_KEY)
|
|
if (stored === 'dark') return true
|
|
if (stored === 'light') return false
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
}
|
|
return false
|
|
}
|
|
|
|
const title = ref('HnH Map')
|
|
const dark = ref(false)
|
|
const live = ref(false)
|
|
const me = ref<{ username?: string; auths?: string[] } | null>(null)
|
|
|
|
const { isLoginPath } = useAppPaths()
|
|
const isLogin = computed(() => isLoginPath(route.path))
|
|
const isAdmin = computed(() => !!me.value?.auths?.includes('admin'))
|
|
|
|
async function loadMe() {
|
|
if (isLogin.value) return
|
|
try {
|
|
me.value = await useMapApi().me()
|
|
} catch {
|
|
me.value = null
|
|
}
|
|
}
|
|
|
|
async function loadConfig() {
|
|
if (isLogin.value) return
|
|
try {
|
|
const config = await useMapApi().getConfig()
|
|
if (config?.title) title.value = config.title
|
|
} catch (_) {}
|
|
}
|
|
|
|
onMounted(() => {
|
|
dark.value = getInitialDark()
|
|
const html = document.documentElement
|
|
html.setAttribute('data-theme', dark.value ? 'dark' : 'light')
|
|
})
|
|
|
|
watch(
|
|
() => route.path,
|
|
(path) => {
|
|
if (!isLoginPath(path)) loadMe().then(loadConfig)
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
function toggleTheme() {
|
|
const html = document.documentElement
|
|
if (dark.value) {
|
|
html.setAttribute('data-theme', 'dark')
|
|
localStorage.setItem(THEME_KEY, 'dark')
|
|
} else {
|
|
html.setAttribute('data-theme', 'light')
|
|
localStorage.setItem(THEME_KEY, 'light')
|
|
}
|
|
}
|
|
|
|
async function doLogout() {
|
|
await useMapApi().logout()
|
|
await router.push('/login')
|
|
me.value = null
|
|
}
|
|
|
|
defineExpose({ setLive: (v: boolean) => { live.value = v } })
|
|
</script>
|