Files
pokopiawiki.tootaio.com/frontend/src/App.vue
xiaomai 6758aaaa7e feat(home): add home page as main entry point
Introduce HomeView with quick links to wiki sections and community features
Update navigation, routing, and logo links to point to the new home page
2026-05-03 17:46:36 +08:00

146 lines
4.0 KiB
Vue

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import AppShell from './components/AppShell.vue';
import {
iconAction,
iconAdmin,
iconAutomation,
iconChecklist,
iconClothes,
iconDish,
iconDreamIsland,
iconEvent,
iconHabitat,
iconHome,
iconItem,
iconLife,
iconPokemon,
iconRecipe
} from './icons';
import { getCurrentLocale, loadSystemWordings, onLocaleChange, setCurrentLocale } from './i18n';
import { api, getAuthToken, onAuthTokenChange, setAuthToken, type AuthUser, type Language } from './services/api';
const { t, locale } = useI18n();
const router = useRouter();
const currentUser = ref<AuthUser | null>(null);
const languages = ref<Language[]>([
{ code: 'en', name: 'English', enabled: true, isDefault: true, sortOrder: 10 },
{ code: 'zh-CN', name: '简体中文', enabled: true, isDefault: false, sortOrder: 20 }
]);
let removeAuthListener: (() => void) | null = null;
let removeLocaleListener: (() => void) | null = null;
function inDevBadge() {
return { label: t('common.inDev'), tone: 'info' as const };
}
function can(permissionKey: string) {
return currentUser.value?.permissions.includes(permissionKey) === true;
}
const navItems = computed(() => {
const items = [
{ label: t('nav.home'), to: '/', icon: iconHome },
{ label: t('nav.pokemon'), to: '/pokemon', icon: iconPokemon },
{ label: t('nav.habitats'), to: '/habitats', icon: iconHabitat },
{ label: t('nav.items'), to: '/items', icon: iconItem },
{ label: t('nav.recipes'), to: '/recipes', icon: iconRecipe },
{ label: t('nav.automation'), to: '/automation', icon: iconAutomation, badge: inDevBadge() },
{ label: t('nav.dish'), to: '/dish', icon: iconDish, badge: inDevBadge() },
{ label: t('nav.events'), to: '/events', icon: iconEvent, badge: inDevBadge() },
{ label: t('nav.actions'), to: '/actions', icon: iconAction, badge: inDevBadge() },
{ label: t('nav.dreamIsland'), to: '/dream-island', icon: iconDreamIsland, badge: inDevBadge() },
{ label: t('nav.clothes'), to: '/clothes', icon: iconClothes, badge: inDevBadge() },
{ label: t('nav.checklist'), to: '/checklist', icon: iconChecklist },
{ label: t('nav.life'), to: '/life', icon: iconLife }
];
if (can('admin.access')) {
items.push({ label: t('nav.admin'), to: '/admin', icon: iconAdmin });
}
return items;
});
async function loadCurrentUser() {
if (!getAuthToken()) {
currentUser.value = null;
return;
}
try {
const response = await api.me();
currentUser.value = response.user;
} catch {
currentUser.value = null;
setAuthToken(null);
}
}
async function logout() {
try {
await api.logout();
} catch {
// The local session is cleared even when the server session is already gone.
}
currentUser.value = null;
setAuthToken(null);
await router.push('/');
}
async function loadLanguages() {
try {
const loadedLanguages = await api.languages();
if (loadedLanguages.length) {
languages.value = loadedLanguages;
}
if (!languages.value.some((language) => language.code === getCurrentLocale() && language.enabled)) {
setCurrentLocale('en');
}
await loadSystemWordings(getCurrentLocale());
} catch {
// Keep the built-in language list when the API is not ready yet.
}
}
async function updateLocale(value: string) {
await loadSystemWordings(value);
setCurrentLocale(value);
}
onMounted(() => {
void loadLanguages();
void loadCurrentUser();
removeAuthListener = onAuthTokenChange(() => {
void loadCurrentUser();
});
removeLocaleListener = onLocaleChange(() => {
void loadLanguages();
});
});
onUnmounted(() => {
removeAuthListener?.();
removeLocaleListener?.();
});
</script>
<template>
<AppShell
:current-user="currentUser"
:languages="languages"
:locale="locale"
:nav-items="navItems"
@logout="logout"
@update:locale="updateLocale"
>
<RouterView :key="locale" />
</AppShell>
</template>