Files
pricing.tootaio.com/app/composables/eventOrder.ts
xiaomai eb69f6c48e 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.
2025-10-16 15:22:29 +08:00

95 lines
3.0 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 * 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<string[]>([...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<typeof orderSchema>;
const orderState = reactive<OrderForm>({
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);