update: mobile navbar

This commit is contained in:
Tony Yang
2025-04-17 11:30:07 +08:00
parent dbb4598164
commit d90d2c5e3c
4 changed files with 38 additions and 13 deletions
+20 -11
View File
@@ -24,7 +24,7 @@ function logout() {
<template>
<nav>
<div class="ts-container navbar-container">
<div class="ts-container navbar-container" v-if="!$isMobile">
<div class="ts-wrap">
<RouterLink class="ts-header is-brand" to="/">網路攻防實習</RouterLink>
<div class="ts-tab is-tall">
@@ -43,6 +43,25 @@ function logout() {
</template>
</div>
</div>
<div class="ts-container" v-else>
<div class="ts-wrap navbar-container">
<RouterLink class="ts-header is-brand has-top-padded has-bottom-padded" to="/">網路攻防實習</RouterLink>
<div class="actions ts-wrap">
<template v-if="isLoggedIn">
<span class="username">{{ username }}</span>
<RouterLink class="ts-button is-icon is-outlined" to="/profile"><span class="ts-icon is-user-icon"></span></RouterLink>
<div class="ts-button" @click="logout">登出</div>
</template>
<template v-else>
<RouterLink class="ts-button" to="/login">登入</RouterLink>
<RouterLink class="ts-button" to="/register">註冊</RouterLink>
</template>
</div>
</div>
<div class="ts-tab is-tall is-center-aligned">
<RouterLink class="item" :to="route.path" :class="{'is-active': route.path == $route.path}" v-for="route in $router.options.routes.filter(route => route.meta.showInNav)">{{ route.meta.navName }}</RouterLink>
</div>
</div>
</nav>
</template>
@@ -66,14 +85,4 @@ nav {
display: flex;
align-items: center;
}
@media screen and (max-width: 768px) {
.navbar-container {
flex-direction: column;
}
.actions {
margin: 10px 0;
}
}
</style>
+14
View File
@@ -0,0 +1,14 @@
import { reactive } from 'vue';
const mobileMediaQuery = 'screen and (max-width: 768px)';
const matchMedia = window.matchMedia(mobileMediaQuery);
const isMobile = reactive(matchMedia.matches);
matchMedia.addEventListener('change', (event) => {
isMobile = event.matches;
});
export const install = (app) => {
app.config.globalProperties.$isMobile = isMobile;
}
+2
View File
@@ -5,6 +5,7 @@ import router from './router';
import { createPinia } from 'pinia';
import { install as installRecaptcha } from "vue3-recaptcha-v2";
import { install as installDarkMode } from './lib/darkMode';
import { install as installIsMobile } from './lib/isMobile';
const pinia = createPinia();
const app = createApp(App);
@@ -12,6 +13,7 @@ const app = createApp(App);
app.use(pinia);
app.use(router);
app.use(installDarkMode);
app.use(installIsMobile);
app.use(installRecaptcha, {
sitekey: import.meta.env.VITE_RECAPTCHA_SITEKEY
});
+2 -2
View File
@@ -38,7 +38,7 @@ const generateMotto = async () => {
<template>
<div class="ts-app-center">
<div>
<div class="ts-content is-horizontally-fitted">
<div class="ts-box ts-content is-center-aligned">
<div class="ts-header is-large is-center-aligned">每日金句生成器</div>
<div class="ts-header is-secondary is-center-aligned">Powered By Cloudflare Workers AI</div>
@@ -53,7 +53,7 @@ const generateMotto = async () => {
<button class="ts-button" @click="generateMotto" :disabled="!captchaVerified">生成</button>
</div>
<div class="ts-content is-center-aligned">
<p class="ts-text">每日金句:</p>
<p class="ts-text" v-if="mottoLoading || motto">每日金句:</p>
<div class="ts-loading" v-if="mottoLoading"></div>
<div class="ts-quote" v-if="motto">
<p class="ts-text">{{ motto }}</p>