"use client"; import { ArrowLeft, ArrowRight, Check, CheckCircle2 } from "lucide-react"; import { useRouter } from "next/navigation"; import { useEffect, useRef, useState } from "react"; import { useToast } from "@/components/Toast"; import { Badge } from "@/components/ui/Badge"; import { Button } from "@/components/ui/Button"; import { Field } from "@/components/ui/Field"; import { SegmentedControl } from "@/components/ui/SegmentedControl"; import { Select } from "@/components/ui/Select"; import { TextArea } from "@/components/ui/TextArea"; import { TextInput } from "@/components/ui/TextInput"; import { api } from "@/lib/api/client"; import { buttonClass } from "@/lib/ui/variants"; import { GENRES, SELLING_POINT_PRESETS, STEP_TITLES, STRUCTURES, WIZARD_STEPS, canAdvance, canSubmit, clampStep, emptyWizardForm, toCreateRequest, type WizardForm, } from "@/lib/wizard/wizard"; // 立项向导(UX §6.2)。Client Component:收集字段 → POST /projects → 进工作台。 export function ProjectWizard() { const router = useRouter(); const toast = useToast(); const [step, setStep] = useState(1); const [form, setForm] = useState(emptyWizardForm); const [submitting, setSubmitting] = useState(false); // 换步后把焦点移到新步标题,方便键盘/读屏用户感知步骤变化;首帧不抢第 1 步输入框的 autoFocus。 const stepHeadingRef = useRef(null); const isFirstRender = useRef(true); useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; } stepHeadingRef.current?.focus(); }, [step]); const update = (patch: Partial): void => setForm((prev) => ({ ...prev, ...patch })); const toggleSellingPoint = (point: string): void => setForm((prev) => ({ ...prev, sellingPoints: prev.sellingPoints.includes(point) ? prev.sellingPoints.filter((p) => p !== point) : [...prev.sellingPoints, point], })); const goBack = (): void => setStep((s) => clampStep(s - 1)); const goNext = (): void => setStep((s) => clampStep(s + 1)); const submit = async (): Promise => { if (!canSubmit(form)) { toast("请先填写书名", "error"); setStep(1); return; } setSubmitting(true); const { data, error } = await api.POST("/projects", { body: toCreateRequest(form), }); if (error || !data) { setSubmitting(false); toast("创建作品失败,请重试", "error"); return; } router.push(`/projects/${data.id}/write`); }; const isLast = step === WIZARD_STEPS; return (

新建作品

步骤 {step}/{WIZARD_STEPS}

{STEP_TITLES[step - 1]}

{step === 1 && } {step === 2 && ( )} {step === 3 && } {step === 4 && } {step === 5 && }
{isLast ? ( ) : ( )}
); } function StepDots({ current }: { current: number }) { return ( ); } interface StepProps { form: WizardForm; update: (patch: Partial) => void; } function StepBasics({ form, update }: StepProps) { const titleMissing = form.title.trim().length === 0; return (
update({ title: e.target.value })} placeholder="例:逐光而行" autoFocus />
); } function StepStory({ form, update, toggleSellingPoint, }: StepProps & { toggleSellingPoint: (p: string) => void }) { return (