Files
writer-work-flow/apps/web/lib/wizard/wizard.ts

80 lines
2.5 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 type { ProjectCreateRequest } from "@/lib/api/types";
// 立项向导 5 步UX §6.2)。表单状态用字符串/数组,提交时归一为 ProjectCreateRequest。
export interface WizardForm {
title: string;
genre: string;
logline: string;
sellingPoints: string[];
structure: string;
premise: string;
protagonist: string;
theme: string;
}
export const WIZARD_STEPS = 5;
// 各步标题(与 WIZARD_STEPS 一一对应)。第 5 步为「确认/概览」:回显已填字段供复核后提交。
export const STEP_TITLES = [
"书名 + 题材",
"一句话故事 + 卖点 + 结构",
"立意 / 总纲",
"主角 / 金手指",
"确认与提交",
] as const;
export const emptyWizardForm: WizardForm = {
title: "",
genre: "",
logline: "",
sellingPoints: [],
structure: "",
premise: "",
protagonist: "",
theme: "",
};
export const GENRES = ["玄幻", "仙侠", "都市", "科幻", "历史", "悬疑"];
export const STRUCTURES = ["三幕", "故事圈", "雪花"];
export const SELLING_POINT_PRESETS = ["逆袭", "打脸", "系统流", "双男主", "群像"];
// 每一步可否前进:仅第 1 步(书名)必填,其余可空着继续(草稿式立项)。
export function canAdvance(step: number, form: WizardForm): boolean {
if (step === 1) return form.title.trim().length > 0;
return true;
}
// 整个向导可否提交:书名必填。
export function canSubmit(form: WizardForm): boolean {
return form.title.trim().length > 0;
}
export function clampStep(step: number): number {
if (step < 1) return 1;
if (step > WIZARD_STEPS) return WIZARD_STEPS;
return step;
}
// 归一为后端请求体snake_case空值落 null/省略)。
export function toCreateRequest(form: WizardForm): ProjectCreateRequest {
const trim = (s: string): string | null => {
const v = s.trim();
return v.length > 0 ? v : null;
};
// 立意premise与主角/金手指protagonist是两个独立输入但 M1 projects 表只有
// premise 一个字段 → 合并进 premise二者各占一段互不覆盖QA H1此前共用同一字段
const premise =
[trim(form.premise), form.protagonist.trim() ? `主角/金手指:${form.protagonist.trim()}` : null]
.filter((s): s is string => s !== null)
.join("\n\n") || null;
return {
title: form.title.trim(),
genre: trim(form.genre),
logline: trim(form.logline),
premise,
theme: trim(form.theme),
selling_points: form.sellingPoints,
structure: trim(form.structure),
};
}