refactor(frontend): migrate from Vite to Nuxt SPA

Replace Vite and Vue Router with Nuxt framework
Update Docker, build scripts, and env vars for Nuxt generate
This commit is contained in:
2026-05-06 09:19:23 +08:00
parent c821e9ebba
commit 6e8edbbb09
76 changed files with 7045 additions and 640 deletions

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ComingSoonView from '../src/views/ComingSoonView.vue';
definePageMeta({
name: 'actions',
seo: { titleKey: 'pages.comingSoon.sections.actions.title', descriptionKey: 'pages.comingSoon.sections.actions.subtitle', noindex: true }
});
</script>
<template>
<ComingSoonView page="actions" />
</template>

13
frontend/pages/admin.vue Normal file
View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import AdminView from '../src/views/AdminView.vue';
definePageMeta({
name: 'admin',
requiredPermission: 'admin.access',
seo: { titleKey: 'pages.admin.title', descriptionKey: 'pages.admin.subtitle', noindex: true }
});
</script>
<template>
<AdminView />
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import ItemDetail from '../../../src/views/ItemDetail.vue';
definePageMeta({
name: 'ancient-artifact-edit',
requiredPermission: 'items.update',
editorModal: true,
seo: {
titleKey: 'pages.ancientArtifacts.editKicker',
descriptionKey: 'pages.ancientArtifacts.editSubtitle',
canonicalPath: (route: RouteLocationNormalizedLoaded) => `/ancient-artifacts/${String(route.params.id)}`,
noindex: true
}
});
</script>
<template>
<ItemDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ItemDetail from '../../../src/views/ItemDetail.vue';
definePageMeta({
name: 'ancient-artifact-detail',
seo: { titleKey: 'pages.ancientArtifacts.detailKicker', descriptionKey: 'pages.ancientArtifacts.subtitle' }
});
</script>
<template>
<ItemDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import AncientArtifactList from '../../src/views/AncientArtifactList.vue';
definePageMeta({
name: 'ancient-artifact-list',
seo: { titleKey: 'pages.ancientArtifacts.title', descriptionKey: 'pages.ancientArtifacts.subtitle' }
});
</script>
<template>
<AncientArtifactList />
</template>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
import AncientArtifactList from '../../src/views/AncientArtifactList.vue';
definePageMeta({
name: 'ancient-artifact-new',
requiredPermission: 'items.create',
editorModal: true,
seo: {
titleKey: 'pages.ancientArtifacts.newTitle',
descriptionKey: 'pages.ancientArtifacts.editSubtitle',
canonicalPath: '/ancient-artifacts',
noindex: true
}
});
</script>
<template>
<AncientArtifactList />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ComingSoonView from '../src/views/ComingSoonView.vue';
definePageMeta({
name: 'automation',
seo: { titleKey: 'pages.comingSoon.sections.automation.title', descriptionKey: 'pages.comingSoon.sections.automation.subtitle', noindex: true }
});
</script>
<template>
<ComingSoonView page="automation" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import DailyChecklistView from '../src/views/DailyChecklistView.vue';
definePageMeta({
name: 'checklist',
seo: { titleKey: 'pages.checklist.title', descriptionKey: 'pages.checklist.subtitle' }
});
</script>
<template>
<DailyChecklistView />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ComingSoonView from '../src/views/ComingSoonView.vue';
definePageMeta({
name: 'clothes',
seo: { titleKey: 'pages.comingSoon.sections.clothes.title', descriptionKey: 'pages.comingSoon.sections.clothes.subtitle', noindex: true }
});
</script>
<template>
<ComingSoonView page="clothes" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import LegalView from '../src/views/LegalView.vue';
definePageMeta({
name: 'disclaimers',
seo: { titleKey: 'pages.legal.disclaimers.title', descriptionKey: 'pages.legal.disclaimers.subtitle', canonicalPath: '/disclaimers' }
});
</script>
<template>
<LegalView page="disclaimers" />
</template>

12
frontend/pages/dish.vue Normal file
View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import DishView from '../src/views/DishView.vue';
definePageMeta({
name: 'dish',
seo: { titleKey: 'pages.dish.title', descriptionKey: 'pages.dish.subtitle' }
});
</script>
<template>
<DishView />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ComingSoonView from '../src/views/ComingSoonView.vue';
definePageMeta({
name: 'dream-island',
seo: { titleKey: 'pages.comingSoon.sections.dreamIsland.title', descriptionKey: 'pages.comingSoon.sections.dreamIsland.subtitle', noindex: true }
});
</script>
<template>
<ComingSoonView page="dreamIsland" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import HabitatList from '../../src/views/HabitatList.vue';
definePageMeta({
name: 'event-habitat-list',
seo: { titleKey: 'pages.eventHabitats.title', descriptionKey: 'pages.eventHabitats.subtitle', canonicalPath: '/event-habitats' }
});
</script>
<template>
<HabitatList :event-only="true" />
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import HabitatList from '../../src/views/HabitatList.vue';
definePageMeta({
name: 'event-habitat-new',
requiredPermission: 'habitats.create',
editorModal: true,
seo: { titleKey: 'pages.eventHabitats.newTitle', descriptionKey: 'pages.eventHabitats.editSubtitle', canonicalPath: '/event-habitats', noindex: true }
});
</script>
<template>
<HabitatList :event-only="true" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ItemsList from '../../src/views/ItemsList.vue';
definePageMeta({
name: 'event-item-list',
seo: { titleKey: 'pages.eventItems.title', descriptionKey: 'pages.eventItems.subtitle', canonicalPath: '/event-items' }
});
</script>
<template>
<ItemsList :event-only="true" />
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import ItemsList from '../../src/views/ItemsList.vue';
definePageMeta({
name: 'event-item-new',
requiredPermission: 'items.create',
editorModal: true,
seo: { titleKey: 'pages.eventItems.newTitle', descriptionKey: 'pages.eventItems.editSubtitle', canonicalPath: '/event-items', noindex: true }
});
</script>
<template>
<ItemsList :event-only="true" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import PokemonList from '../../src/views/PokemonList.vue';
definePageMeta({
name: 'event-pokemon-list',
seo: { titleKey: 'pages.eventPokemon.title', descriptionKey: 'pages.eventPokemon.subtitle', canonicalPath: '/event-pokemon' }
});
</script>
<template>
<PokemonList :event-only="true" />
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import PokemonList from '../../src/views/PokemonList.vue';
definePageMeta({
name: 'event-pokemon-new',
requiredPermission: 'pokemon.create',
editorModal: true,
seo: { titleKey: 'pages.eventPokemon.newTitle', descriptionKey: 'pages.eventPokemon.editSubtitle', canonicalPath: '/event-pokemon', noindex: true }
});
</script>
<template>
<PokemonList :event-only="true" />
</template>

12
frontend/pages/events.vue Normal file
View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ComingSoonView from '../src/views/ComingSoonView.vue';
definePageMeta({
name: 'events',
seo: { titleKey: 'pages.comingSoon.sections.events.title', descriptionKey: 'pages.comingSoon.sections.events.subtitle', noindex: true }
});
</script>
<template>
<ComingSoonView page="events" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ForgotPasswordView from '../src/views/ForgotPasswordView.vue';
definePageMeta({
name: 'forgot-password',
seo: { titleKey: 'auth.requestResetTitle', descriptionKey: 'auth.requestResetSubtitle', noindex: true }
});
</script>
<template>
<ForgotPasswordView />
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import HabitatDetail from '../../../src/views/HabitatDetail.vue';
definePageMeta({
name: 'habitat-edit',
requiredPermission: 'habitats.update',
editorModal: true,
seo: {
titleKey: 'pages.habitats.detailKicker',
descriptionKey: 'pages.habitats.editSubtitle',
canonicalPath: (route: RouteLocationNormalizedLoaded) => `/habitats/${String(route.params.id)}`,
noindex: true
}
});
</script>
<template>
<HabitatDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import HabitatDetail from '../../../src/views/HabitatDetail.vue';
definePageMeta({
name: 'habitat-detail',
seo: { titleKey: 'pages.habitats.detailKicker', descriptionKey: 'pages.habitats.subtitle' }
});
</script>
<template>
<HabitatDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import HabitatList from '../../src/views/HabitatList.vue';
definePageMeta({
name: 'habitat-list',
seo: { titleKey: 'pages.habitats.title', descriptionKey: 'pages.habitats.subtitle' }
});
</script>
<template>
<HabitatList :event-only="false" />
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import HabitatList from '../../src/views/HabitatList.vue';
definePageMeta({
name: 'habitat-new',
requiredPermission: 'habitats.create',
editorModal: true,
seo: { titleKey: 'pages.habitats.newTitle', descriptionKey: 'pages.habitats.editSubtitle', canonicalPath: '/habitats', noindex: true }
});
</script>
<template>
<HabitatList :event-only="false" />
</template>

12
frontend/pages/index.vue Normal file
View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import HomeView from '../src/views/HomeView.vue';
definePageMeta({
name: 'home',
seo: { titleKey: 'pages.home.title', descriptionKey: 'pages.home.subtitle', canonicalPath: '/' }
});
</script>
<template>
<HomeView />
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import ItemDetail from '../../../src/views/ItemDetail.vue';
definePageMeta({
name: 'item-edit',
requiredPermission: 'items.update',
editorModal: true,
seo: {
titleKey: 'pages.items.editKicker',
descriptionKey: 'pages.items.editSubtitle',
canonicalPath: (route: RouteLocationNormalizedLoaded) => `/items/${String(route.params.id)}`,
noindex: true
}
});
</script>
<template>
<ItemDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ItemDetail from '../../../src/views/ItemDetail.vue';
definePageMeta({
name: 'item-detail',
seo: { titleKey: 'pages.items.detailKicker', descriptionKey: 'pages.items.subtitle' }
});
</script>
<template>
<ItemDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ItemsList from '../../src/views/ItemsList.vue';
definePageMeta({
name: 'item-list',
seo: { titleKey: 'pages.items.title', descriptionKey: 'pages.items.subtitle' }
});
</script>
<template>
<ItemsList :event-only="false" />
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import ItemsList from '../../src/views/ItemsList.vue';
definePageMeta({
name: 'item-new',
requiredPermission: 'items.create',
editorModal: true,
seo: { titleKey: 'pages.items.newTitle', descriptionKey: 'pages.items.editSubtitle', canonicalPath: '/items', noindex: true }
});
</script>
<template>
<ItemsList :event-only="false" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import LifePostDetail from '../../src/views/LifePostDetail.vue';
definePageMeta({
name: 'life-id',
seo: { titleKey: 'pages.life.detailTitle', descriptionKey: 'pages.life.detailSubtitle' }
});
</script>
<template>
<LifePostDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import LifeView from '../../src/views/LifeView.vue';
definePageMeta({
name: 'life',
seo: { titleKey: 'pages.life.title', descriptionKey: 'pages.life.subtitle' }
});
</script>
<template>
<LifeView />
</template>

12
frontend/pages/login.vue Normal file
View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import LoginView from '../src/views/LoginView.vue';
definePageMeta({
name: 'login',
seo: { titleKey: 'auth.loginTitle', descriptionKey: 'auth.loginSubtitle', noindex: true }
});
</script>
<template>
<LoginView />
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import PokemonDetail from '../../../src/views/PokemonDetail.vue';
definePageMeta({
name: 'pokemon-edit',
requiredPermission: 'pokemon.update',
editorModal: true,
seo: {
titleKey: 'pages.pokemon.editKicker',
descriptionKey: 'pages.pokemon.editSubtitle',
canonicalPath: (route: RouteLocationNormalizedLoaded) => `/pokemon/${String(route.params.id)}`,
noindex: true
}
});
</script>
<template>
<PokemonDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import PokemonDetail from '../../../src/views/PokemonDetail.vue';
definePageMeta({
name: 'pokemon-detail',
seo: { titleKey: 'pages.pokemon.detailKicker', descriptionKey: 'pages.pokemon.subtitle' }
});
</script>
<template>
<PokemonDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import PokemonList from '../../src/views/PokemonList.vue';
definePageMeta({
name: 'pokemon-list',
seo: { titleKey: 'pages.pokemon.title', descriptionKey: 'pages.pokemon.subtitle' }
});
</script>
<template>
<PokemonList :event-only="false" />
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import PokemonList from '../../src/views/PokemonList.vue';
definePageMeta({
name: 'pokemon-new',
requiredPermission: 'pokemon.create',
editorModal: true,
seo: { titleKey: 'pages.pokemon.newTitle', descriptionKey: 'pages.pokemon.editSubtitle', canonicalPath: '/pokemon', noindex: true }
});
</script>
<template>
<PokemonList :event-only="false" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import LegalView from '../src/views/LegalView.vue';
definePageMeta({
name: 'privacy-policy',
seo: { titleKey: 'pages.legal.privacy.title', descriptionKey: 'pages.legal.privacy.subtitle', canonicalPath: '/privacy-policy' }
});
</script>
<template>
<LegalView page="privacy" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import UserProfileView from '../../src/views/UserProfileView.vue';
definePageMeta({
name: 'profile-id',
seo: { titleKey: 'pages.profile.title', descriptionKey: 'pages.profile.publicSubtitle' }
});
</script>
<template>
<UserProfileView />
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import UserProfileView from '../../src/views/UserProfileView.vue';
definePageMeta({
name: 'profile',
requiresAuth: true,
seo: { titleKey: 'pages.profile.title', descriptionKey: 'pages.profile.subtitle', noindex: true }
});
</script>
<template>
<UserProfileView />
</template>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import ProjectUpdatesView from '../src/views/ProjectUpdatesView.vue';
definePageMeta({
name: 'project-updates',
seo: {
titleKey: 'pages.projectUpdates.title',
descriptionKey: 'pages.projectUpdates.subtitle',
canonicalPath: '/project-updates'
}
});
</script>
<template>
<ProjectUpdatesView />
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import RecipeDetail from '../../../src/views/RecipeDetail.vue';
definePageMeta({
name: 'recipe-edit',
requiredPermission: 'recipes.update',
editorModal: true,
seo: {
titleKey: 'pages.recipes.editKicker',
descriptionKey: 'pages.recipes.editSubtitle',
canonicalPath: (route: RouteLocationNormalizedLoaded) => `/recipes/${String(route.params.id)}`,
noindex: true
}
});
</script>
<template>
<RecipeDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import RecipeDetail from '../../../src/views/RecipeDetail.vue';
definePageMeta({
name: 'recipe-detail',
seo: { titleKey: 'pages.recipes.detailKicker', descriptionKey: 'pages.recipes.subtitle' }
});
</script>
<template>
<RecipeDetail />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import RecipeList from '../../src/views/RecipeList.vue';
definePageMeta({
name: 'recipe-list',
seo: { titleKey: 'pages.recipes.title', descriptionKey: 'pages.recipes.subtitle' }
});
</script>
<template>
<RecipeList />
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import RecipeList from '../../src/views/RecipeList.vue';
definePageMeta({
name: 'recipe-new',
requiredPermission: 'recipes.create',
editorModal: true,
seo: { titleKey: 'pages.recipes.newTitle', descriptionKey: 'pages.recipes.editSubtitle', canonicalPath: '/recipes', noindex: true }
});
</script>
<template>
<RecipeList />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import RegisterView from '../src/views/RegisterView.vue';
definePageMeta({
name: 'register',
seo: { titleKey: 'auth.registerTitle', descriptionKey: 'auth.registerSubtitle', noindex: true }
});
</script>
<template>
<RegisterView />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ResetPasswordView from '../src/views/ResetPasswordView.vue';
definePageMeta({
name: 'reset-password',
seo: { titleKey: 'auth.resetTitle', descriptionKey: 'auth.resetSubtitle', noindex: true }
});
</script>
<template>
<ResetPasswordView />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import LegalView from '../src/views/LegalView.vue';
definePageMeta({
name: 'terms-of-service',
seo: { titleKey: 'pages.legal.terms.title', descriptionKey: 'pages.legal.terms.subtitle', canonicalPath: '/terms-of-service' }
});
</script>
<template>
<LegalView page="terms" />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import VerifyEmailView from '../src/views/VerifyEmailView.vue';
definePageMeta({
name: 'verify-email',
seo: { titleKey: 'auth.verifyTitle', descriptionKey: 'auth.verifySubtitle', noindex: true }
});
</script>
<template>
<VerifyEmailView />
</template>