diff --git a/src/api/modules/studio.ts b/src/api/modules/studio.ts index 94dad79..3f0da93 100644 --- a/src/api/modules/studio.ts +++ b/src/api/modules/studio.ts @@ -36,6 +36,20 @@ export const fetchStylePresets = (params: ListParams) => export const updateRobotAuthor = (id: string, payload: Partial) => api.put>(`${BASE}/robot-authors/${id}`, payload) +export interface ArticlePreview { + kind: 'studio_article_preview' + article_id: string + version_status: 'draft' | 'published' | null + title: string | null + summary: string | null + cover_image: { url: string; alt?: string | null } | null + body_html: string + has_content: boolean +} + +export const fetchArticlePreview = (id: string) => + api.get>(`${BASE}/articles/${id}/preview`) + // ---- agent tokens (show-once) ---- export const fetchAgentTokens = () => diff --git a/src/features/studio/article-preview-sheet.tsx b/src/features/studio/article-preview-sheet.tsx new file mode 100644 index 0000000..8985bec --- /dev/null +++ b/src/features/studio/article-preview-sheet.tsx @@ -0,0 +1,94 @@ +import { useQuery } from '@tanstack/react-query' +import { fetchArticlePreview } from '@/api/modules/studio' +import type { StudioArticle } from '@/api/types' +import { StatusPill } from '@/components/status-pill' +import { Badge } from '@/components/ui/badge' +import { Skeleton } from '@/components/ui/skeleton' +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, +} from '@/components/ui/sheet' + +interface ArticlePreviewSheetProps { + article: StudioArticle | null + onOpenChange: (open: boolean) => void +} + +/** Renders the article's Dimzou draft (title/summary/cover + body HTML). */ +export function ArticlePreviewSheet({ article, onOpenChange }: ArticlePreviewSheetProps) { + const preview = useQuery({ + queryKey: ['studio-article-preview', article?.id], + queryFn: () => fetchArticlePreview(article!.id), + enabled: article !== null, + }) + + const data = preview.data?.data + + return ( + + + + + 草稿预览 + {data?.version_status && ( + + {data.version_status === 'draft' ? '草稿版本' : '已发布版本'} + + )} + {article && } + + + {article?.dimzou_document_id + ? `Dimzou 文档 #${article.dimzou_document_id}` + : '尚未建稿'} + + + +
+ {preview.isPending ? ( +
+ + + + + +
+ ) : !data || (!data.has_content && !data.title) ? ( +

该文章尚无正文内容

+ ) : ( +
+ {data.cover_image?.url && ( + {data.cover_image.alt + )} + {data.title && ( +

+ {data.title} +

+ )} + {data.summary && ( +

{data.summary}

+ )} + {data.has_content ? ( +
+ ) : ( +

(仅有标题/摘要,正文为空)

+ )} +
+ )} +
+
+
+ ) +} diff --git a/src/routes/_authed/studio/articles.tsx b/src/routes/_authed/studio/articles.tsx index 39a4ea4..543121f 100644 --- a/src/routes/_authed/studio/articles.tsx +++ b/src/routes/_authed/studio/articles.tsx @@ -4,6 +4,7 @@ import type { ColumnDef } from '@tanstack/react-table' import type { StudioArticle } from '@/api/types' import { fetchArticles } from '@/api/modules/studio' import { useListPage } from '@/lib/list-page' +import { ArticlePreviewSheet } from '@/features/studio/article-preview-sheet' import { DetailSheet } from '@/features/studio/detail-sheet' import { DataTable } from '@/components/data-table/data-table' import { PageHeader } from '@/components/page-header' @@ -26,6 +27,7 @@ const STATUSES = ['planned', 'drafting', 'drafted', 'published', 'scheduled'] as function ArticlesPage() { const [status, setStatus] = useState('all') const [detail, setDetail] = useState(null) + const [preview, setPreview] = useState(null) const list = useListPage( 'studio-articles', fetchArticles, @@ -67,9 +69,19 @@ function ArticlesPage() { { header: '操作', cell: ({ row }) => ( - +
+ + +
), }, ] @@ -132,6 +144,8 @@ function ArticlesPage() { { label: '发布排期', value: detail?.publish_schedule }, ]} /> + + !open && setPreview(null)} /> ) } diff --git a/src/styles.css b/src/styles.css index a05dbcc..dade75a 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,5 +1,6 @@ @import 'tailwindcss'; @import 'tw-animate-css'; +@plugin "@tailwindcss/typography"; @custom-variant dark (&:is(.dark *));