diff --git a/src/api/modules/studio.ts b/src/api/modules/studio.ts index 3f0da93..8bf2821 100644 --- a/src/api/modules/studio.ts +++ b/src/api/modules/studio.ts @@ -30,6 +30,23 @@ export const fetchSubtopics = (params: ListParams) => export const fetchCategoryBriefs = (params: ListParams) => api.get>(`${BASE}/category-briefs`, params) +export interface CategoryBriefPayload { + category_id?: string + description?: string | null + target_audience?: string | null + content_angles?: string[] | null + risk_notes?: string | null +} + +export const createCategoryBrief = (payload: CategoryBriefPayload) => + api.post>(`${BASE}/category-briefs`, payload) + +export const updateCategoryBrief = (id: string, payload: CategoryBriefPayload) => + api.put>(`${BASE}/category-briefs/${id}`, payload) + +export const deleteCategoryBrief = (id: string) => + api.delete>(`${BASE}/category-briefs/${id}`) + export const fetchStylePresets = (params: ListParams) => api.get>(`${BASE}/style-presets`, params) diff --git a/src/features/studio/category-brief-edit-sheet.tsx b/src/features/studio/category-brief-edit-sheet.tsx new file mode 100644 index 0000000..afe2ac5 --- /dev/null +++ b/src/features/studio/category-brief-edit-sheet.tsx @@ -0,0 +1,83 @@ +import { useEffect, useState } from 'react' +import { toast } from 'sonner' +import type { StudioCategoryBrief } from '@/api/types' +import { + createCategoryBrief, + updateCategoryBrief, + type CategoryBriefPayload, +} from '@/api/modules/studio' +import { handleApiError } from '@/lib/errors' +import { CategoryPicker } from '@/features/categories/category-picker' +import { Button } from '@/components/ui/button' +import { Label } from '@/components/ui/label' +import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet' +import { Textarea } from '@/components/ui/textarea' + +interface Props { + brief: StudioCategoryBrief | null + creating: boolean + onOpenChange: (open: boolean) => void + onSaved: () => void +} + +export function CategoryBriefEditSheet({ brief, creating, onOpenChange, onSaved }: Props) { + const open = creating || brief !== null + const [categoryId, setCategoryId] = useState(null) + const [description, setDescription] = useState('') + const [targetAudience, setTargetAudience] = useState('') + const [contentAngles, setContentAngles] = useState('') + const [riskNotes, setRiskNotes] = useState('') + const [saving, setSaving] = useState(false) + + useEffect(() => { + setCategoryId(brief?.category_id ?? null) + setDescription(brief?.description ?? '') + setTargetAudience(brief?.target_audience ?? '') + setContentAngles(brief?.content_angles?.join('\n') ?? '') + setRiskNotes(brief?.risk_notes ?? '') + }, [brief, creating]) + + const save = async () => { + if (!categoryId) { + toast.error('请选择分类') + return + } + + const payload: CategoryBriefPayload = { + category_id: categoryId, + description: description.trim() || null, + target_audience: targetAudience.trim() || null, + content_angles: contentAngles.split('\n').map((value) => value.trim()).filter(Boolean), + risk_notes: riskNotes.trim() || null, + } + + setSaving(true) + try { + if (brief) await updateCategoryBrief(brief.id, payload) + else await createCategoryBrief(payload) + toast.success(brief ? '分类简报已更新' : '分类简报已创建') + onSaved() + onOpenChange(false) + } catch (error) { + handleApiError(error) + } finally { + setSaving(false) + } + } + + return ( + + + {brief ? '编辑分类简报' : '新建分类简报'} +
+
+