feat(ui): rebuild homepage with new sections and i18n
This commit introduces a complete overhaul of the homepage, rebuilding it from the ground up with Nuxt UI and full internationalization support. The new design better showcases the studio's capabilities and projects. - Re-architected the index page with multiple new sections: Capabilities, Featured Projects, Tech Stack, and Why Choose Us. - Implemented full i18n for English (en-US) and Chinese (zh-CN) across all new content. - Centralized the page structure into a `default.vue` layout with a global header (including color mode and locale selectors) and footer. - Replaced placeholder logos with a dynamic `UMarquee` of technology icons using Iconify.
This commit is contained in:
@@ -1,13 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
<UPage>
|
||||
<UHeader>
|
||||
<template #title> Tootaio Studio </template>
|
||||
<template #right>
|
||||
<UColorModeButton />
|
||||
<ULocaleSelect
|
||||
:model-value="locale"
|
||||
:locales="[en, zh_cn]"
|
||||
@update:model-value="(v) => setLocale(v as 'en' | 'zh-CN')"
|
||||
/>
|
||||
</template>
|
||||
</UHeader>
|
||||
<UMain>
|
||||
<slot />
|
||||
</UMain>
|
||||
<UFooter>
|
||||
<template #left>
|
||||
© {{ new Date().getFullYear() }} Tootaio Studio. All rights
|
||||
reserved.
|
||||
</template>
|
||||
</UFooter>
|
||||
</UPage>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import { en, zh_cn } from "@nuxt/ui/locale";
|
||||
const { locale, setLocale } = useI18n();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<style></style>
|
||||
|
||||
@@ -1,44 +1,236 @@
|
||||
<template>
|
||||
<UPage>
|
||||
<UHeader>
|
||||
<template #title> Tootaio Studio </template>
|
||||
<template #right>
|
||||
<ULocaleSelect
|
||||
:model-value="locale"
|
||||
:locales="[en, zh_cn]"
|
||||
@update:model-value="(v) => setLocale(v as 'en' | 'zh-CN')"
|
||||
/>
|
||||
</template>
|
||||
</UHeader>
|
||||
<div>
|
||||
<!-- 全幅 Hero - 在 Page 布局之外 -->
|
||||
<UPageHero
|
||||
title="Tootaio Studio"
|
||||
:description="$t('index.heroDescription')"
|
||||
/>
|
||||
|
||||
<UPageSection
|
||||
title="Trusted by"
|
||||
:description="$t('index.trustedBy', { count: 10000 })"
|
||||
>
|
||||
:title="$t('index.capabilities.title')"
|
||||
:features="capabilitiesFeatures"
|
||||
/>
|
||||
|
||||
<UPageSection :title="$t('index.featuredProjects.title')">
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
:items="featuredProjects"
|
||||
:ui="{ item: 'basis-full sm:basis-1/2 lg:basis-1/3' }"
|
||||
>
|
||||
<UCard class="my-2">
|
||||
<template #header>
|
||||
<h3 class="text-2xl font-bold">{{ item.title }}</h3>
|
||||
</template>
|
||||
<template #default>
|
||||
<img :src="item.image" :alt="item.title" />
|
||||
<p class="mt-2 line-clamp-3">{{ item.description }}</p>
|
||||
</template>
|
||||
<template #footer>
|
||||
<UButton
|
||||
v-if="item.demoLink"
|
||||
:href="item.demoLink"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
size="sm"
|
||||
>
|
||||
{{ $t("index.featuredProjects.viewDemo") }}
|
||||
</UButton>
|
||||
</template>
|
||||
</UCard>
|
||||
</UCarousel>
|
||||
</UPageSection>
|
||||
|
||||
<UPageSection :title="$t('index.techStack.title')">
|
||||
<UMarquee>
|
||||
<img
|
||||
v-for="logo in trustedBy"
|
||||
:key="logo.src"
|
||||
:src="logo.src"
|
||||
:alt="logo.alt"
|
||||
class="h-12 mx-8 grayscale opacity-60"
|
||||
<UIcon
|
||||
v-for="icon in techIcons"
|
||||
:key="icon"
|
||||
:name="icon"
|
||||
class="size-16"
|
||||
/>
|
||||
</UMarquee>
|
||||
<UMarquee reverse>
|
||||
<UIcon
|
||||
v-for="icon in toolsIcons"
|
||||
:key="icon"
|
||||
:name="icon"
|
||||
class="size-16"
|
||||
/>
|
||||
</UMarquee>
|
||||
</UPageSection>
|
||||
</UPage>
|
||||
|
||||
<UPageSection
|
||||
:title="$t('index.whyChooseUs.title')"
|
||||
:features="whyChooseUsFeatures"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { en, zh_cn } from "@nuxt/ui/locale";
|
||||
const { locale, setLocale } = useI18n();
|
||||
import type { PageFeatureProps } from "@nuxt/ui";
|
||||
|
||||
const trustedBy = ref([
|
||||
{ src: "/index/trusted-by/logoipsum-284.svg", alt: "Logo Ipsum" },
|
||||
{ src: "/index/trusted-by/logoipsum-338.svg", alt: "Logo Ipsum" },
|
||||
{ src: "/index/trusted-by/logoipsum-353.svg", alt: "Logo Ipsum" },
|
||||
{ src: "/index/trusted-by/logoipsum-378.svg", alt: "Logo Ipsum" },
|
||||
{ src: "/index/trusted-by/logoipsum-392.svg", alt: "Logo Ipsum" },
|
||||
{ src: "/index/trusted-by/logoipsum-403.svg", alt: "Logo Ipsum" },
|
||||
{ src: "/index/trusted-by/logoipsum-409.svg", alt: "Logo Ipsum" },
|
||||
useSeoMeta({
|
||||
title: $t("index.seo.title"),
|
||||
});
|
||||
|
||||
const colorMode = useColorMode();
|
||||
|
||||
const capabilitiesFeatures = computed<PageFeatureProps[]>(() => [
|
||||
{
|
||||
title: $t("index.capabilities.features[0].title"),
|
||||
description: $t("index.capabilities.features[0].description"),
|
||||
icon: "mdi:web",
|
||||
},
|
||||
{
|
||||
title: $t("index.capabilities.features[1].title"),
|
||||
description: $t("index.capabilities.features[1].description"),
|
||||
icon: "mdi:cog-outline",
|
||||
},
|
||||
{
|
||||
title: $t("index.capabilities.features[2].title"),
|
||||
description: $t("index.capabilities.features[2].description"),
|
||||
icon: "mdi:gamepad-variant-outline",
|
||||
},
|
||||
{
|
||||
title: $t("index.capabilities.features[3].title"),
|
||||
description: $t("index.capabilities.features[3].description"),
|
||||
icon: "mdi:monitor-dashboard",
|
||||
},
|
||||
{
|
||||
title: $t("index.capabilities.features[4].title"),
|
||||
description: $t("index.capabilities.features[4].description"),
|
||||
icon: "mdi:flask-outline",
|
||||
},
|
||||
{
|
||||
title: $t("index.capabilities.features[5].title"),
|
||||
description: $t("index.capabilities.features[5].description"),
|
||||
icon: "mdi:lightbulb-outline",
|
||||
},
|
||||
]);
|
||||
|
||||
const featuredProjects = ref([
|
||||
{
|
||||
title: "永中校友会官方网站",
|
||||
description:
|
||||
"永平中学校友会官方网站的设计与开发项目,整体价值 RM28,000,由本工作室创办人无偿捐赠予校友会永久使用。",
|
||||
image: "/images/project-showcases/yphs-alumni-website.png",
|
||||
demoLink: "https://yphsalumni.org",
|
||||
},
|
||||
{
|
||||
title: "留华生来华资料汇总",
|
||||
description:
|
||||
"2022 年疫情期间,为马来西亚留学生开发的返校攻略网站。帮助 5000+ 名留学生顺利返校。并获得马来西亚外交部推荐。",
|
||||
image: "/images/project-showcases/malaysia-student-return.png",
|
||||
demoLink: "https://tootaio.github.io",
|
||||
},
|
||||
{
|
||||
title: "光追",
|
||||
description: "基于 Godot 引擎的 2023 年吉比特高校挑战赛参赛作品。",
|
||||
image: "https://img.tootaio.com/i/2025/09/26/j2swgq.png",
|
||||
// demoLink: "https://tootaio.com/projects/ray-tracing",
|
||||
},
|
||||
]);
|
||||
|
||||
const techIcons = computed(() => [
|
||||
"skill-icons:html",
|
||||
"skill-icons:css",
|
||||
"skill-icons:javascript",
|
||||
"skill-icons:typescript",
|
||||
"skill-icons:docker",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:vuejs-dark"
|
||||
: "skill-icons:vuejs-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:nuxtjs-dark"
|
||||
: "skill-icons:nuxtjs-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:tailwindcss-dark"
|
||||
: "skill-icons:tailwindcss-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:nodejs-dark"
|
||||
: "skill-icons:nodejs-light",
|
||||
"skill-icons:cs",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:python-dark"
|
||||
: "skill-icons:python-light",
|
||||
]);
|
||||
|
||||
const toolsIcons = ref([
|
||||
"skill-icons:photoshop",
|
||||
"skill-icons:illustrator",
|
||||
"skill-icons:git",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:vscode-dark"
|
||||
: "skill-icons:vscode-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:visualstudio-dark"
|
||||
: "skill-icons:visualstudio-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:github-dark"
|
||||
: "skill-icons:github-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:godot-dark"
|
||||
: "skill-icons:godot-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:unity-dark"
|
||||
: "skill-icons:unity-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:blender-dark"
|
||||
: "skill-icons:blender-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:androidstudio-dark"
|
||||
: "skill-icons:androidstudio-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:windows-dark"
|
||||
: "skill-icons:windows-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:linux-dark"
|
||||
: "skill-icons:linux-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:apple-dark"
|
||||
: "skill-icons:apple-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:idea-dark"
|
||||
: "skill-icons:idea-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:pycharm-dark"
|
||||
: "skill-icons:pycharm-light",
|
||||
colorMode.value === "dark"
|
||||
? "skill-icons:rider-dark"
|
||||
: "skill-icons:rider-light",
|
||||
]);
|
||||
|
||||
const whyChooseUsFeatures = computed<PageFeatureProps[]>(() => [
|
||||
{
|
||||
title: $t("index.whyChooseUs.features[0].title"),
|
||||
description: $t("index.whyChooseUs.features[0].description"),
|
||||
icon: "mdi:brush-variant", // Fully Custom-Built
|
||||
},
|
||||
{
|
||||
title: $t("index.whyChooseUs.features[1].title"),
|
||||
description: $t("index.whyChooseUs.features[1].description"),
|
||||
icon: "mdi:cog-sync-outline", // Tech-Driven
|
||||
},
|
||||
{
|
||||
title: $t("index.whyChooseUs.features[2].title"),
|
||||
description: $t("index.whyChooseUs.features[2].description"),
|
||||
icon: "mdi:gamepad-variant-outline", // Cross-Domain
|
||||
},
|
||||
{
|
||||
title: $t("index.whyChooseUs.features[3].title"),
|
||||
description: $t("index.whyChooseUs.features[3].description"),
|
||||
icon: "mdi:rocket-launch-outline", // End-to-End Service
|
||||
},
|
||||
{
|
||||
title: $t("index.whyChooseUs.features[4].title"),
|
||||
description: $t("index.whyChooseUs.features[4].description"),
|
||||
icon: "mdi:chart-timeline-variant", // Proven Project Value
|
||||
},
|
||||
{
|
||||
title: $t("index.whyChooseUs.features[5].title"),
|
||||
description: $t("index.whyChooseUs.features[5].description"),
|
||||
icon: "mdi:lightbulb-on-outline", // Future-Oriented
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user