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.
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
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(),
|
||
}),
|
||
},
|
||
});
|