refactor(content): migrate index page to Nuxt Content

This commit refactors the index page to source its content from @nuxt/content, replacing the previous implementation that used i18n
JSON files and hardcoded data within the component.

Key changes:
- Introduced `content.config.ts` to define collections and Zod schemas for type-safe content.
- Moved page content into localized YAML files (`content/en-US/index.yml` and `content/zh-CN/index.yml`).
- Updated `app/pages/index.vue` to fetch data dynamically using `useAsyncData` and `queryCollection`.
- Removed redundant content from i18n JSON files and the Vue component script.

This change decouples content from presentation, improves maintainability, and centralizes content management.
This commit is contained in:
xiaomai
2025-11-05 17:54:59 +08:00
parent 87731a6379
commit 78bc2c34a0
7 changed files with 1353 additions and 1293 deletions

View File

@@ -2,8 +2,8 @@
<div> <div>
<!-- 全幅 Hero - Page 布局之外 --> <!-- 全幅 Hero - Page 布局之外 -->
<UPageHero <UPageHero
title="Tootaio Studio" :title="page?.title"
:description="$t('index.heroDescription')" :description="page?.description"
:ui="{ :ui="{
root: 'relative before:absolute before:inset-0 before:bg-[image:var(--bg-image)] before:bg-cover before:bg-center before:-z-10 before:opacity-40', root: 'relative before:absolute before:inset-0 before:bg-[image:var(--bg-image)] before:bg-cover before:bg-center before:-z-10 before:opacity-40',
}" }"
@@ -13,14 +13,14 @@
/> />
<UPageSection <UPageSection
:title="$t('index.capabilities.title')" :title="page?.capabilities.title"
:features="capabilitiesFeatures" :features="page?.capabilities.features"
/> />
<UPageSection :title="$t('index.featuredProjects.title')"> <UPageSection :title="page?.featuredProjects.title">
<UCarousel <UCarousel
v-slot="{ item }" v-slot="{ item }"
:items="featuredProjects" :items="page?.featuredProjects.projects"
:ui="{ item: 'basis-full sm:basis-1/2 lg:basis-1/3' }" :ui="{ item: 'basis-full sm:basis-1/2 lg:basis-1/3' }"
> >
<UCard class="my-2"> <UCard class="my-2">
@@ -46,7 +46,7 @@
</UCarousel> </UCarousel>
</UPageSection> </UPageSection>
<UPageSection :title="$t('index.techStack.title')"> <UPageSection :title="page?.techStack.title">
<UMarquee> <UMarquee>
<UIcon <UIcon
v-for="icon in techIcons" v-for="icon in techIcons"
@@ -66,17 +66,57 @@
</UPageSection> </UPageSection>
<UPageSection <UPageSection
:title="$t('index.whyChooseUs.title')" :title="page?.whyChooseUs.title"
:features="whyChooseUsFeatures" :features="page?.whyChooseUs.features"
/> />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { PageFeatureProps } from "@nuxt/ui"; import type { Collections } from "@nuxt/content";
const { locale } = useI18n();
const { data: page } = await useAsyncData(
"index-" + locale.value,
async () => {
// Build collection name based on current locale
let localeSuffix = "";
switch (locale.value) {
case "en":
localeSuffix = "en";
break;
case "zh-CN":
localeSuffix = "zh";
break;
default:
localeSuffix = "en";
break;
}
const collection = ("index_" + localeSuffix) as keyof Collections;
const content = await queryCollection(collection).first();
// Optional: fallback to default locale if content is missing
if (!content && locale.value !== "en") {
return await queryCollection("index_en").first();
}
return content;
},
{
watch: [locale], // Refetch when locale changes
}
);
if (!page.value) {
throw createError({
statusCode: 404,
statusMessage: `Page not found, the index_${locale.value} couldn't be found.`,
fatal: true,
});
}
useSeoMeta({ useSeoMeta({
title: $t("index.seo.title"), title: page.value?.seo.title,
}); });
const colorMode = useColorMode(); const colorMode = useColorMode();
@@ -94,7 +134,7 @@ const backgroundImages = [
"http://img.tootaio.com/i/2025/11/05/avf3wl.png", "http://img.tootaio.com/i/2025/11/05/avf3wl.png",
]; ];
const currentBgImage = ref(""); const currentBgImage = ref<string | undefined>("");
// 随机选择背景 // 随机选择背景
const randomBg = () => { const randomBg = () => {
@@ -107,62 +147,6 @@ onMounted(() => {
randomBg(); randomBg();
}); });
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: "http://img.tootaio.com/i/2025/11/05/d9kurl.png",
demoLink: "https://yphsalumni.org",
},
{
title: "留华生来华资料汇总",
description:
"2022 年疫情期间,为马来西亚留学生开发的返校攻略网站。帮助 5000+ 名留学生顺利返校。并获得马来西亚外交部推荐。",
image: "http://img.tootaio.com/i/2025/11/05/d9kcma.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(() => [ const techIcons = computed(() => [
"skill-icons:html", "skill-icons:html",
"skill-icons:css", "skill-icons:css",
@@ -231,39 +215,6 @@ const toolsIcons = ref([
? "skill-icons:rider-dark" ? "skill-icons:rider-dark"
: "skill-icons:rider-light", : "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> </script>
<style></style> <style></style>

86
content.config.ts Normal file
View File

@@ -0,0 +1,86 @@
import { defineContentConfig, defineCollection, z } from "@nuxt/content";
export default defineContentConfig({
collections: {
index_en: defineCollection({
type: "page",
source: "en-US/index.yml",
schema: z.object({
capabilities: z.object({
title: z.string(),
features: z.array(
z.object({
title: z.string(),
description: z.string(),
icon: z.string(),
})
),
}),
featuredProjects: z.object({
title: z.string(),
projects: z.array(
z.object({
title: z.string(),
description: z.string(),
image: z.string(),
demoLink: z.string(),
})
),
}),
techStack: z.object({
title: z.string(),
}),
whyChooseUs: z.object({
title: z.string(),
features: z.array(
z.object({
title: z.string(),
description: z.string(),
icon: z.string(),
})
),
}),
}),
}),
index_zh: defineCollection({
type: "page",
source: "zh-CN/index.yml",
schema: z.object({
capabilities: z.object({
title: z.string(),
features: z.array(
z.object({
title: z.string(),
description: z.string(),
icon: z.string(),
})
),
}),
featuredProjects: z.object({
title: z.string(),
projects: z.array(
z.object({
title: z.string(),
description: z.string(),
image: z.string(),
demoLink: z.string(),
})
),
}),
techStack: z.object({
title: z.string(),
}),
whyChooseUs: z.object({
title: z.string(),
features: z.array(
z.object({
title: z.string(),
description: z.string(),
icon: z.string(),
})
),
}),
}),
}),
},
});

View File

@@ -33,37 +33,7 @@ featuredProjects:
image: "https://img.tootaio.com/i/2025/09/26/j2swgq.png" image: "https://img.tootaio.com/i/2025/09/26/j2swgq.png"
techStack: techStack:
title: "Tech Stacks" title: "Tech Stacks"
languageIcons: whyChooseUs:
- light: "skill-icons:html"
- light: "skill-icons:css"
- light: "skill-icons:javascript"
- light: "skill-icons:typescript"
- light: "skill-icons:docker"
- light: "skill-icons:vuejs-light"
- light: "skill-icons:nuxtjs-light"
- light: "skill-icons:tailwindcss-light"
- light: "skill-icons:nodejs-light"
- light: "skill-icons:cs"
- light: "skill-icons:python-light"
toolsIcons:
- light: "skill-icons:photoshop"
- light: "skill-icons:illustrator"
- light: "skill-icons:git"
- light: "skill-icons:vscode-light"
- light: "skill-icons:visualstudio-light"
- light: "skill-icons:github-light"
- light: "skill-icons:godot-light"
- light: "skill-icons:unity-light"
- light: "skill-icons:blender-light"
- light: "skill-icons:androidstudio-light"
- light: "skill-icons:windows-light"
- light: "skill-icons:linux-light"
- light: "skill-icons:ubuntu-light"
- light: "skill-icons:apple-light"
- light: "skill-icons:idea-light"
- light: "skill-icons:pycharm-light"
- light: "skill-icons:rider-light"
capabilities:
title: "Why Choose Us" title: "Why Choose Us"
description: "We dont just build code — we craft digital experiences." description: "We dont just build code — we craft digital experiences."
features: features:

View File

@@ -7,16 +7,22 @@ capabilities:
features: features:
- title: "网站定制开发" - title: "网站定制开发"
description: "基于 Nuxt / Next 等现代框架,为企业打造高性能、可扩展且视觉出众的专属网站与后台系统。" description: "基于 Nuxt / Next 等现代框架,为企业打造高性能、可扩展且视觉出众的专属网站与后台系统。"
icon: mdi:web
- title: "软件与工具工程" - title: "软件与工具工程"
description: "为企业定制自动化工具、数据面板与业务流程系统,提高工作效率与可靠性。" description: "为企业定制自动化工具、数据面板与业务流程系统,提高工作效率与可靠性。"
icon: mdi:cog-outline
- title: "游戏设计与开发" - title: "游戏设计与开发"
description: "从 Game Jam 原型到商业发行,打造富有创意与技术深度的互动体验。" description: "从 Game Jam 原型到商业发行,打造富有创意与技术深度的互动体验。"
icon: mdi:gamepad-variant-outline
- title: "互动媒体与宴会系统" - title: "互动媒体与宴会系统"
description: "为展会、活动与宴会定制实时交互内容与大型屏幕视觉展示。" description: "为展会、活动与宴会定制实时交互内容与大型屏幕视觉展示。"
icon: mdi:monitor-dashboard
- title: "技术探索与评测" - title: "技术探索与评测"
description: "研究与评估前沿软硬件技术,保持创新优势与研发热情。" description: "研究与评估前沿软硬件技术,保持创新优势与研发热情。"
icon: mdi:flask-outline
- title: "创意咨询与数字策略" - title: "创意咨询与数字策略"
description: "为品牌与团队提供产品架构、数字化转型与长期技术规划咨询。" description: "为品牌与团队提供产品架构、数字化转型与长期技术规划咨询。"
icon: mdi:lightbulb-outline
featuredProjects: featuredProjects:
title: "特色项目" title: "特色项目"
projects: projects:
@@ -33,49 +39,25 @@ featuredProjects:
image: "https://img.tootaio.com/i/2025/09/26/j2swgq.png" image: "https://img.tootaio.com/i/2025/09/26/j2swgq.png"
techStack: techStack:
title: "Tech Stacks" title: "Tech Stacks"
languageIcons: whyChooseUs:
- light: "skill-icons:html"
- light: "skill-icons:css"
- light: "skill-icons:javascript"
- light: "skill-icons:typescript"
- light: "skill-icons:docker"
- light: "skill-icons:vuejs-light"
- light: "skill-icons:nuxtjs-light"
- light: "skill-icons:tailwindcss-light"
- light: "skill-icons:nodejs-light"
- light: "skill-icons:cs"
- light: "skill-icons:python-light"
toolsIcons:
- light: "skill-icons:photoshop"
- light: "skill-icons:illustrator"
- light: "skill-icons:git"
- light: "skill-icons:vscode-light"
- light: "skill-icons:visualstudio-light"
- light: "skill-icons:github-light"
- light: "skill-icons:godot-light"
- light: "skill-icons:unity-light"
- light: "skill-icons:blender-light"
- light: "skill-icons:androidstudio-light"
- light: "skill-icons:windows-light"
- light: "skill-icons:linux-light"
- light: "skill-icons:ubuntu-light"
- light: "skill-icons:apple-light"
- light: "skill-icons:idea-light"
- light: "skill-icons:pycharm-light"
- light: "skill-icons:rider-light"
capabilities:
title: "为什么选择我们" title: "为什么选择我们"
description: "我们不仅编写代码——我们打造数字体验。" description: "我们不仅编写代码——我们打造数字体验。"
features: features:
- title: "完全定制开发" - title: "完全定制开发"
description: "我们从不使用模板。每一个网站、系统、游戏都从零设计与开发,确保风格、性能与体验完全符合品牌个性。" description: "我们从不使用模板。每一个网站、系统、游戏都从零设计与开发,确保风格、性能与体验完全符合品牌个性。"
icon: mdi:brush-variant
- title: "技术驱动,而非仅仅是设计" - title: "技术驱动,而非仅仅是设计"
description: "作为开发导向的团队,我们理解底层逻辑。从架构、性能、安全到交互动画,所有细节都由工程师主导优化。" description: "作为开发导向的团队,我们理解底层逻辑。从架构、性能、安全到交互动画,所有细节都由工程师主导优化。"
icon: mdi:cog-sync-outline
- title: "跨领域专长" - title: "跨领域专长"
description: "我们横跨网站、游戏、工具与交互内容开发,让每个项目都能获得更广阔的技术整合思路。" description: "我们横跨网站、游戏、工具与交互内容开发,让每个项目都能获得更广阔的技术整合思路。"
icon: mdi:gamepad-variant-outline
- title: "端到端一站式服务" - title: "端到端一站式服务"
description: "从概念、原型、前端到部署与长期维护,我们全程负责,让客户专注业务,而非技术问题。" description: "从概念、原型、前端到部署与长期维护,我们全程负责,让客户专注业务,而非技术问题。"
icon: mdi:rocket-launch-outline
- title: "经验证的项目价值" - title: "经验证的项目价值"
description: "我们曾为教育机构、品牌活动、独立游戏等开发高价值系统,实力可见。" description: "我们曾为教育机构、品牌活动、独立游戏等开发高价值系统,实力可见。"
icon: mdi:chart-timeline-variant
- title: "面向未来" - title: "面向未来"
description: "我们在研发自己的产品与工具,不止接案,也在打造未来生态。这代表我们具备持续创新与自我进化的能力。" description: "我们在研发自己的产品与工具,不止接案,也在打造未来生态。这代表我们具备持续创新与自我进化的能力。"
icon: mdi:lightbulb-on-outline

View File

@@ -1,75 +1,8 @@
{ {
"index": { "index": {
"trustedBy": "Trusted by over {count} users worldwide", "trustedBy": "Trusted by over {count} users worldwide",
"heroDescription": "Technology Meets Imagination.",
"capabilities": {
"title": "Our Capabilities",
"features": [
{
"title": "Custom Website Development",
"description": "Tailored websites and backend systems built with modern frameworks like Nuxt and Next, delivering performance, scalability, and visual impact."
},
{
"title": "Software & Tool Engineering",
"description": "Custom-built applications, internal dashboards, and automation tools that streamline workflows and enhance productivity."
},
{
"title": "Game Design & Development",
"description": "From Game Jam prototypes to commercial releases — we design and develop immersive, creative, and technically robust gaming experiences."
},
{
"title": "Interactive Media & Event Systems",
"description": "Creating dynamic, large-screen visuals and real-time interactive systems for events, exhibitions, and banquets."
},
{
"title": "Tech Exploration & Evaluation",
"description": "Experimenting with emerging technologies and evaluating software and hardware to stay ahead of the innovation curve."
},
{
"title": "Creative Consulting & Digital Strategy",
"description": "Providing expert guidance on product architecture, digital transformation, and long-term technology strategy for brands and startups."
}
]
},
"techStack": {
"title": "Tech Stacks"
},
"featuredProjects": { "featuredProjects": {
"title": "Featured Projects",
"viewDemo": "Visit Site" "viewDemo": "Visit Site"
},
"whyChooseUs": {
"title": "Why Choose Us",
"subtitle": "We dont just build code — we craft digital experiences.",
"features": [
{
"title": "Fully Custom-Built",
"description": "Every website, system, and game is built from scratch to match your unique brand identity and performance needs."
},
{
"title": "Tech-Driven, Not Just Design",
"description": "Our engineers lead every project, optimizing architecture, performance, and security at every level."
},
{
"title": "Cross-Domain Expertise",
"description": "From web systems to games and interactive tools — we merge creativity and engineering to deliver seamless experiences."
},
{
"title": "End-to-End Service",
"description": "From concept, prototyping, and frontend to deployment and long-term support — we handle everything in-house."
},
{
"title": "Proven Project Value",
"description": "Weve delivered projects for educational institutions, brands, and game developers — with real impact and measurable value."
},
{
"title": "Future-Oriented",
"description": "Beyond client work, were building our own products and experiments, pushing the boundaries of whats possible."
}
]
},
"seo": {
"title": "Homepage"
} }
} }
} }

View File

@@ -1,75 +1,8 @@
{ {
"index": { "index": {
"trustedBy": "全球有超过 {count} 用户信赖", "trustedBy": "全球有超过 {count} 用户信赖",
"heroDescription": "专为想要独特体验的品牌打造量身定制的数字产品。",
"capabilities": {
"title": "我们的核心能力",
"features": [
{
"title": "网站定制开发",
"description": "基于 Nuxt / Next 等现代框架,为企业打造高性能、可扩展且视觉出众的专属网站与后台系统。"
},
{
"title": "软件与工具工程",
"description": "为企业定制自动化工具、数据面板与业务流程系统,提高工作效率与可靠性。"
},
{
"title": "游戏设计与开发",
"description": "从 Game Jam 原型到商业发行,打造富有创意与技术深度的互动体验。"
},
{
"title": "互动媒体与宴会系统",
"description": "为展会、活动与宴会定制实时交互内容与大型屏幕视觉展示。"
},
{
"title": "技术探索与评测",
"description": "研究与评估前沿软硬件技术,保持创新优势与研发热情。"
},
{
"title": "创意咨询与数字策略",
"description": "为品牌与团队提供产品架构、数字化转型与长期技术规划咨询。"
}
]
},
"techStack": {
"title": "我们的技术栈"
},
"featuredProjects": { "featuredProjects": {
"title": "特色项目",
"viewDemo": "访问页面" "viewDemo": "访问页面"
},
"whyChooseUs": {
"title": "为什么选择我们",
"subtitle": "我们不仅编写代码——我们打造数字体验。",
"features": [
{
"title": "完全定制开发",
"description": "我们从不使用模板。每一个网站、系统、游戏都从零设计与开发,确保风格、性能与体验完全符合品牌个性。"
},
{
"title": "技术驱动,而非仅仅是设计",
"description": "作为开发导向的团队,我们理解底层逻辑。从架构、性能、安全到交互动画,所有细节都由工程师主导优化。"
},
{
"title": "跨领域专长",
"description": "我们横跨网站、游戏、工具与交互内容开发,让每个项目都能获得更广阔的技术整合思路。"
},
{
"title": "端到端一站式服务",
"description": "从概念、原型、前端到部署与长期维护,我们全程负责,让客户专注业务,而非技术问题。"
},
{
"title": "经验证的项目价值",
"description": "我们曾为教育机构、品牌活动、独立游戏等开发高价值系统,实力可见。"
},
{
"title": "面向未来",
"description": "我们在研发自己的产品与工具,不止接案,也在打造未来生态。这代表我们具备持续创新与自我进化的能力。"
}
]
},
"seo": {
"title": "首页"
} }
} }
} }

2197
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff