"use client"; import { useCallback, useEffect, useState } from "react"; type Recipe = { positioning: string; audience: string; tone: string; contentFormat: string; titleMethod: string; bodyStructure: string; defaultTags: string[]; }; type Project = { id: string; name: string; description: string; recipe: Recipe; revision: number; updatedAt: string; }; async function api(url: string, init?: RequestInit): Promise { const response = await fetch(url, { ...init, headers: { "Content-Type": "application/json", ...(init?.headers || {}) } }); const data = await response.json(); if (!response.ok) throw new Error(data.error || `请求失败:${response.status}`); return data; } const emptyRecipe: Recipe = { positioning: "", audience: "", tone: "真诚自然", contentFormat: "干货教程", titleMethod: "", bodyStructure: "", defaultTags: [], }; export default function Studio() { const [projects, setProjects] = useState([]); const [selected, setSelected] = useState(null); const [draft, setDraft] = useState(null); const [busy, setBusy] = useState(""); const [message, setMessage] = useState("项目是最高层级:一套项目,对应一种稳定的内容生成方式。"); const loadProjects = useCallback(async () => { const data = await api<{ projects: Project[] }>("/api/projects"); setProjects(data.projects); setSelected((current) => current || data.projects[0] || null); }, []); useEffect(() => { void loadProjects(); }, [loadProjects]); useEffect(() => { setDraft(selected ? structuredClone(selected) : null); }, [selected]); async function createProject() { setBusy("create"); try { const data = await api<{ project: Project }>("/api/projects", { method: "POST", body: JSON.stringify({ name: `内容项目 ${projects.length + 1}` }), }); setProjects((items) => [data.project, ...items]); setSelected(data.project); setMessage("项目骨架已创建,请完善这类内容的统一生成方法。"); } catch (error) { setMessage((error as Error).message); } finally { setBusy(""); } } async function saveProject() { if (!draft) return; setBusy("save"); try { const data = await api<{ project: Project }>(`/api/projects/${draft.id}`, { method: "PUT", body: JSON.stringify({ ...draft, revision: draft.revision }), }); setSelected(data.project); setProjects((items) => items.map((item) => item.id === data.project.id ? data.project : item)); setMessage(`项目方法已保存 · r${data.project.revision}`); } catch (error) { setMessage((error as Error).message); } finally { setBusy(""); } } function updateRecipe(key: keyof Recipe, value: string | string[]) { setDraft((current) => current ? { ...current, recipe: { ...emptyRecipe, ...current.recipe, [key]: value } } : current); } return
R
Rednote Content Studio小红书内容生成器 · 起步结构
{!draft ?
✦

先创建一个项目

为一类内容定义固定的定位、受众、语气和结构。

: <>
项目配置

{draft.name}

{draft.description}

基本信息