Files
tootaio.com/content.config.ts
xiaomai 8cc04b7f59 feat(pages): add web development services page
This commit introduces a new page at `/webDev` to display web development services and pricing plans.

To support this, a new reusable composable `useLocalizedCollection` has been created to simplify fetching localized content from Nuxt
Content. The index page has been refactored to use this new composable.

- Adds `webDev.vue` page and corresponding `webDev.yml` content files for EN and ZH.
- Defines a Zod schema in `content.config.ts` for the new content type.
- Updates the navigation link to point to the new page.
2025-11-06 09:02:50 +08:00

115 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineContentConfig, defineCollection, z } from "@nuxt/content";
const defineIndexSchema = () =>
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(),
highlight: z.boolean(),
spotlight: z.boolean(),
})
),
}),
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(),
})
),
}),
});
const defineWebDevSchema = () =>
z.object({
remarks: z.string(),
services: z.array(
z.object({
id: z.string().min(1),
label: z.string().min(1),
icon: z.string().optional(), // 比如 "lucide:mouse-pointer-click"
// 你原结构里通过 createService 包装,但最终是一个对象
plans: z
.array(
z.object({
title: z.string().min(1),
description: z.string().optional(),
price: z.string().optional(), // 保持为 string例如 "RM499 起"),如需更严可用 regex
discount: z.string().optional(),
tagline: z.string().optional(),
highlight: z.boolean().optional().default(false),
features: z.array(z.string()).optional().default([]),
button: z
.object({
label: z.string().min(1),
href: z.string().url().optional(), // 如果你可能会传链接
// 预留扩展字段例如variant、target 等)
variant: z
.enum([
"link",
"solid",
"outline",
"soft",
"subtle",
"ghost",
])
.default("subtle"),
target: z.enum(["_self", "_blank"]).optional(),
})
.partial({ href: true, variant: true, target: true }) // 全部可选除 label如果你希望 label 也可选,可把 min(1) 去掉或改 optional
.optional(),
})
)
.min(1),
// 预留扩展字段例如category、tags、hidden 等)
category: z.string().optional(),
tags: z.array(z.string()).optional(),
})
),
});
export default defineContentConfig({
collections: {
index_en: defineCollection({
type: "page",
source: "en-US/index.yml",
schema: defineIndexSchema(),
}),
index_zh: defineCollection({
type: "page",
source: "zh-CN/index.yml",
schema: defineIndexSchema(),
}),
webDev_en: defineCollection({
type: "page",
source: "en-US/webDev.yml",
schema: defineWebDevSchema(),
}),
webDev_zh: defineCollection({
type: "page",
source: "zh-CN/webDev.yml",
schema: defineWebDevSchema(),
}),
},
});