From eb69f6c48e77b2e571868c2a6ba20bdde0e42e30 Mon Sep 17 00:00:00 2001 From: xiaomai Date: Thu, 16 Oct 2025 15:22:29 +0800 Subject: [PATCH] feat: initialize project with event order form This commit establishes the initial structure of the Nuxt application, centered around a new event order form. Key changes include: - Setting up the main app layout with a header, footer, and color mode support using @nuxt/ui. - Creating a multi-part event order form on the index page. - Introducing a `useEventOrder` composable to manage form state and validation with Zod. - Adding modular form components under `app/components/eventOrder`. - Including project configuration files for pnpm, VSCode, and global CSS. --- .github/copilot-instructions.md | 41 ++++++++ .npmrc | 1 + .vscode/settings.json | 24 +++++ app/app.vue | 9 +- app/assets/css/main.css | 2 + app/components/eventOrder/MetaDetails.vue | 68 ++++++++++++++ .../eventOrder/ProductBackgroundDesign.vue | 78 +++++++++++++++ app/components/eventOrder/ProductBidding.vue | 54 +++++++++++ .../eventOrder/ProductFlowDesign.vue | 65 +++++++++++++ app/components/eventOrder/ProductSponsor.vue | 25 +++++ app/composables/eventOrder.ts | 94 +++++++++++++++++++ app/layouts/default.vue | 59 ++++++++++++ app/pages/index.vue | 29 ++++++ nuxt.config.ts | 1 + pnpm-workspace.yaml | 5 + 15 files changed, 551 insertions(+), 4 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 .npmrc create mode 100644 .vscode/settings.json create mode 100644 app/assets/css/main.css create mode 100644 app/components/eventOrder/MetaDetails.vue create mode 100644 app/components/eventOrder/ProductBackgroundDesign.vue create mode 100644 app/components/eventOrder/ProductBidding.vue create mode 100644 app/components/eventOrder/ProductFlowDesign.vue create mode 100644 app/components/eventOrder/ProductSponsor.vue create mode 100644 app/composables/eventOrder.ts create mode 100644 app/layouts/default.vue create mode 100644 app/pages/index.vue create mode 100644 pnpm-workspace.yaml diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..c99c08a --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,41 @@ +This repository is a small Nuxt 4 site (TypeScript + Vue 3) using the `@nuxt/ui` component kit. + +Quick context +- Framework: Nuxt 4 (see `nuxt.config.ts`). compatibilityDate is 2025-07-15 and `@nuxt/ui` is enabled. +- Scripts: `dev`, `build`, `generate`, `preview` are defined in `package.json`. Use `pnpm` in this workspace (pnpm-lock present). +- UI: Pages and layout live under `app/` (Nuxt App Directory). Components from `@nuxt/ui` (prefixed with `U*`) are used throughout. + +What an AI coding agent should know/do first +- Run local dev: `pnpm dev` to start Nuxt dev server (http://localhost:3000). Changes in `app/` hot-reload. +- Preserve TypeScript usage: files use `lang="ts"` in SFCs and `typescript` is a dependency. +- Styling: `app/assets/css/main.css` imports Tailwind and `@nuxt/ui`. Avoid removing these imports. + +Project-specific patterns and conventions +- Use Nuxt app directory layout: `app/app.vue`, `app/layouts/default.vue`, `app/pages/*.vue`. Add pages/components under `app/`. +- UI primitives: Expect `UApp`, `UHeader`, `UMain`, `UFooter`, `UPageHero`, `UContainer`, `UForm`, `UCard`, `UInput`, `USelect`, etc. These are provided by `@nuxt/ui` — prefer those over adding new UI libraries. +- Minimal global JS: logic is inside pages via ` + + diff --git a/app/components/eventOrder/ProductBackgroundDesign.vue b/app/components/eventOrder/ProductBackgroundDesign.vue new file mode 100644 index 0000000..7d25683 --- /dev/null +++ b/app/components/eventOrder/ProductBackgroundDesign.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/app/components/eventOrder/ProductBidding.vue b/app/components/eventOrder/ProductBidding.vue new file mode 100644 index 0000000..3bbf9ba --- /dev/null +++ b/app/components/eventOrder/ProductBidding.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/app/components/eventOrder/ProductFlowDesign.vue b/app/components/eventOrder/ProductFlowDesign.vue new file mode 100644 index 0000000..ebcacd4 --- /dev/null +++ b/app/components/eventOrder/ProductFlowDesign.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/app/components/eventOrder/ProductSponsor.vue b/app/components/eventOrder/ProductSponsor.vue new file mode 100644 index 0000000..ee0efbb --- /dev/null +++ b/app/components/eventOrder/ProductSponsor.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/app/composables/eventOrder.ts b/app/composables/eventOrder.ts new file mode 100644 index 0000000..40fdee2 --- /dev/null +++ b/app/composables/eventOrder.ts @@ -0,0 +1,94 @@ +import * as z from "zod"; +import { createSharedComposable } from "@vueuse/core"; + +export const _useEventOrder = () => { + const sectionIndex = ref(0); + + function formatLocalDate(date: Date): string { + const y = date.getFullYear(); + const m = String(date.getMonth() + 1).padStart(2, "0"); + const d = String(date.getDate()).padStart(2, "0"); + return `${y}-${m}-${d}`; + } + + const EVENT_LOCATIONS = [ + "永平区", + "周边城市(Batu Pahat / Kluang)", + "柔佛州境内", + "柔佛州境外", + ] as const; + + const eventLocationItems = ref([...EVENT_LOCATIONS]); + + const orderSchema = z.object({ + contactName: z.string().min(1, "姓名不能为空"), + // 用户可以选择是否使用同一联系方式 + isSameContact: z.boolean().default(true), + contactNumber: z.string().optional(), + eventName: z.string().min(1, "活动名称不能为空"), + eventDate: z + .string() + .refine((date) => { + return !isNaN(Date.parse(date)); + }, "无效的日期") + .refine((date) => { + const eventDate = new Date(date); + const today = new Date(); + // 仅按日期比较(忽略时间) + eventDate.setHours(0, 0, 0, 0); + today.setHours(0, 0, 0, 0); + // 活动日期必须是今天或未来的日期 + return eventDate.getTime() >= today.getTime(); + }, "活动日期必须是今天或未来的日期"), + eventLocation: z.enum(EVENT_LOCATIONS), + // 服务:竞标系统 + biddingSystem: z.boolean().default(false), + biddingSystemProvideImage: z.boolean().default(false).optional(), + estimatedBidItemCount: z.string().optional(), + // 服务:背景设计 + backgroundDesign: z.boolean().default(false), + backgroundType: z.enum(["static", "dynamic"]).optional(), + backgroundWidthOverride: z.number().optional(), + backgroundHeightOverride: z.number().optional(), + // 服务:流程 PPT 设计 + flowBackgroundDesign: z.boolean().default(false), + backgroundSourceProvided: z.boolean().default(false), // 如果自己设计的,那么就没有这个额外收费 + pptDesignQty: z + .number() + .min(1, "最少都要一张") + .max(20, "太多了我来不及做设计"), + // 服务:赞助商征信录设计 + sponsorListDesign: z.boolean().default(false), + }); + + type OrderForm = z.infer; + + const orderState = reactive({ + contactName: "", + isSameContact: true, + contactNumber: "", + eventName: "", + eventDate: formatLocalDate(new Date()), + eventLocation: EVENT_LOCATIONS[0], + biddingSystem: false, + biddingSystemProvideImage: false, + estimatedBidItemCount: "30-40", + backgroundDesign: false, + backgroundType: "static", + backgroundWidthOverride: 1920, + backgroundHeightOverride: 1080, + flowBackgroundDesign: false, + backgroundSourceProvided: false, + pptDesignQty: 1, + sponsorListDesign: false, + }); + + return { + sectionIndex, + eventLocationItems, + orderSchema, + orderState, + }; +}; + +export const useEventOrder = createSharedComposable(_useEventOrder); diff --git a/app/layouts/default.vue b/app/layouts/default.vue new file mode 100644 index 0000000..4807a40 --- /dev/null +++ b/app/layouts/default.vue @@ -0,0 +1,59 @@ + + + diff --git a/app/pages/index.vue b/app/pages/index.vue new file mode 100644 index 0000000..dca64bb --- /dev/null +++ b/app/pages/index.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/nuxt.config.ts b/nuxt.config.ts index 9ce9cf0..3f94e4e 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -3,4 +3,5 @@ export default defineNuxtConfig({ compatibilityDate: "2025-07-15", devtools: { enabled: true }, modules: ["@nuxt/ui"], + css: ["~/assets/css/main.css"], }); diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..c7aa842 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +onlyBuiltDependencies: + - '@parcel/watcher' + - '@tailwindcss/oxide' + - esbuild + - vue-demi