From 014fa59090c556d95cc922886185c01d497761f2 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Tue, 15 Sep 2026 18:51:21 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix(generate):=20#1800=20=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E7=94=9F=E6=88=90=E8=BF=9B=E5=BA=A6=E5=8D=A1=E7=89=87=E5=B8=83?= =?UTF-8?q?=E5=B1=80=E4=BF=AE=E5=A4=8D=20-=20=E7=A7=BB=E9=99=A4=20inline?= =?UTF-8?q?=20grid/maxWidth=EF=BC=8C=E6=94=BE=E5=AE=BD=E5=88=97=E5=AE=BD?= =?UTF-8?q?=20minmax(300,360)=EF=BC=8C=E6=A0=87=E9=A2=98=E5=9B=BE=E6=A0=87?= =?UTF-8?q?/=E6=96=87=E6=9C=AC=E6=8B=86=E5=88=86=20flex=20=E9=98=B2?= =?UTF-8?q?=E6=BA=A2=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/BatchGenerationGrid.tsx | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/apps/web/src/pages/generate/components/BatchGenerationGrid.tsx b/apps/web/src/pages/generate/components/BatchGenerationGrid.tsx index 0e2d5b0fb..b0ff6801e 100644 --- a/apps/web/src/pages/generate/components/BatchGenerationGrid.tsx +++ b/apps/web/src/pages/generate/components/BatchGenerationGrid.tsx @@ -1,20 +1,26 @@ /** - * 第5步「确认生成」— 批量渲染进度网格(Issue #1677) + * 第5步「确认生成」— 批量渲染进度网格(Issue #1677 / #1800 布局修复) * * N 个正式生成任务各自独立卡片:进度条 / 成功成片播放 / 失败原因 + 单独重试。 * 数据来自 useGenerateVideo 的 batchTasks(useGenerationPolling 实时回传)。 + * + * #1800 修复点: + * - 不再用 inline style 硬编码 grid 列宽 / 卡片 maxWidth,改由 CSS 统一控制 + * (便于响应式 + 避免 inline 覆盖类规则)。 + * - 标题图标 + 文本拆分为独立 span,文本 span 加 flex:1/min-width:0/ellipsis, + * 防止长标题在窄列里溢出导致与相邻卡片进度条视觉重叠。 */ -import React from "react" -import { LoadingOutlined, CheckCircleFilled, CloseCircleOutlined } from "@ant-design/icons" -import type { BatchTaskState } from "../hooks/generate-video/useGenerationPolling" -import type { GeneratedVideo } from "@/api/template-editor" +import React from "react"; +import { LoadingOutlined, CheckCircleFilled, CloseCircleOutlined } from "@ant-design/icons"; +import type { BatchTaskState } from "../hooks/generate-video/useGenerationPolling"; +import type { GeneratedVideo } from "@/api/template-editor"; interface BatchGenerationGridProps { - tasks: BatchTaskState[] + tasks: BatchTaskState[]; /** 变体标题(按变体序号取) */ - titles: string[] + titles: string[]; /** 失败任务重试 */ - onRetryTask: (taskId: string) => void + onRetryTask: (taskId: string) => void; } const BatchGenerationGrid: React.FC = ({ @@ -24,7 +30,7 @@ const BatchGenerationGrid: React.FC = ({ }) => { const sorted = [...tasks].sort( (a, b) => Number(a.variantIndex || 0) - Number(b.variantIndex || 0), - ) + ); return (
@@ -34,36 +40,34 @@ const BatchGenerationGrid: React.FC = ({ 完成 {tasks.filter((t) => t.status === "completed").length} / {tasks.length}
-
+ {/* #1800: grid 列宽 / gap / justify 全部交由 .xx-batch-gen-grid CSS 控制 */} +
{sorted.map((task) => { - const title = titles[task.variantIndex] || `视频 ${task.variantIndex + 1}` - const video = (task.videos?.[0] || null) as GeneratedVideo | null + const title = titles[task.variantIndex] || `视频 ${task.variantIndex + 1}`; + const video = (task.videos?.[0] || null) as GeneratedVideo | null; return ( -
+
{task.status === "completed" ? ( - + ) : task.status === "failed" ? ( - + ) : ( - + )} - 视频 {task.variantIndex + 1}:{title} + + 视频 {task.variantIndex + 1}:{title} +
@@ -124,11 +128,11 @@ const BatchGenerationGrid: React.FC = ({ )}
- ) + ); })}
- ) -} + ); +}; -export default BatchGenerationGrid +export default BatchGenerationGrid; -- 2.54.0 From 526e25d70a49615429dccb3999ce832434d11cd8 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Tue, 15 Sep 2026 18:51:22 +0800 Subject: [PATCH 2/6] =?UTF-8?q?fix(generate):=20#1800=20=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E5=8D=A1=E7=89=87=20CSS=20=E9=87=8D=E5=86=99?= =?UTF-8?q?=20-=20grid=20=E5=88=97=E5=AE=BD=E6=94=BE=E5=AE=BD=E3=80=81?= =?UTF-8?q?=E5=8D=A1=E7=89=87=20width:100%=E3=80=81=E6=A0=87=E9=A2=98=20fl?= =?UTF-8?q?ex+ellipsis=20=E5=85=9C=E5=BA=95=E3=80=81=E7=AA=84=E5=B1=8F?= =?UTF-8?q?=E5=8D=95=E5=88=97=E4=B8=8A=E9=99=90=20360?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/generate/generate.css | 35 ++++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/apps/web/src/pages/generate/generate.css b/apps/web/src/pages/generate/generate.css index 0f8a5c2df..cb5eff27e 100644 --- a/apps/web/src/pages/generate/generate.css +++ b/apps/web/src/pages/generate/generate.css @@ -3376,14 +3376,18 @@ } /* ============================================================ - 第5步确认生成:批量渲染进度网格(Issue #1677) + 第5步确认生成:批量渲染进度网格(Issue #1677 / #1800 布局修复) + - 列宽从 minmax(160,180) 放宽到 minmax(300,360),避免进度条/标题挤压 + - 卡片 width:100% 撑满列宽,避免 inline maxWidth 硬编码 + - 标题图标/文本拆分 flex 布局,文本 ellipsis 长标题不溢出 ============================================================ */ .xx-batch-gen-grid { - justify-items: center; display: grid; - grid-template-columns: repeat(auto-fill, minmax(160px, 180px)); + grid-template-columns: repeat(auto-fill, minmax(300px, 360px)); justify-content: center; + justify-items: stretch; gap: 14px; + width: 100%; } .xx-batch-gen-card { @@ -3394,7 +3398,9 @@ display: flex; flex-direction: column; gap: 10px; + width: 100%; min-width: 0; + box-sizing: border-box; } .xx-batch-gen-card.status-completed { @@ -3410,14 +3416,30 @@ .xx-batch-gen-card-head { display: flex; align-items: center; - justify-content: space-between; gap: 8px; + min-width: 0; } .xx-batch-gen-card-title { + display: flex; + align-items: center; + gap: 6px; font-size: 14px; font-weight: 600; color: var(--text-primary, #1a1a1a); + flex: 1; + min-width: 0; + overflow: hidden; +} + +.xx-batch-gen-card-icon { + flex-shrink: 0; + font-size: 14px; +} + +.xx-batch-gen-card-title-text { + flex: 1; + min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -3427,6 +3449,7 @@ display: flex; flex-direction: column; gap: 8px; + min-width: 0; } .xx-batch-gen-card-pct { @@ -3455,10 +3478,10 @@ word-break: break-word; } -/* ── 响应式:窄屏批量网格回退单列(.xx-canvas-grid 的窄屏限宽见网格定义处 #1741) ── */ +/* ── 响应式:窄屏批量网格回退单列 ── */ @media (max-width: 960px) { .xx-batch-gen-grid { justify-items: center; - grid-template-columns: minmax(0, 320px); + grid-template-columns: minmax(0, 360px); } } -- 2.54.0 From 90a0d55c34d589a5ad3bac6ca3f4bf6996b27b50 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Tue, 15 Sep 2026 18:51:23 +0800 Subject: [PATCH 3/6] =?UTF-8?q?fix(scripts):=20#1893=20AI=20=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=20UX=20=E7=BB=86=E8=8A=82=E4=BF=AE=E5=A4=8D=20-=20dis?= =?UTF-8?q?abled=20=E6=8C=89=E9=92=AE=E7=94=A8=20Tooltip+span=20=E5=8C=85?= =?UTF-8?q?=E8=A3=B9=E4=BF=9D=E8=AF=81=E6=8F=90=E7=A4=BA=E5=8F=AF=E8=A7=81?= =?UTF-8?q?=E3=80=81content=20=E4=B8=BA=E7=A9=BA=E6=97=B6=E7=A6=81?= =?UTF-8?q?=E7=94=A8=E6=94=B9=E5=86=99=E6=8C=89=E9=92=AE=E5=B9=B6=E9=87=8D?= =?UTF-8?q?=E7=BD=AE=E6=97=A7=E7=BB=93=E6=9E=9C=E3=80=81=E6=94=B9=E5=86=99?= =?UTF-8?q?=20loading=20=E6=97=B6=E7=A6=81=E5=85=B3=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E3=80=81=E9=A3=8E=E6=A0=BC=E9=80=89=E6=8B=A9=20loading=20?= =?UTF-8?q?=E7=A6=81=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/scripts/ScriptLibrary.tsx | 409 ++++++++++--------- 1 file changed, 217 insertions(+), 192 deletions(-) diff --git a/apps/web/src/pages/scripts/ScriptLibrary.tsx b/apps/web/src/pages/scripts/ScriptLibrary.tsx index 1cc40c92a..b74dc7296 100644 --- a/apps/web/src/pages/scripts/ScriptLibrary.tsx +++ b/apps/web/src/pages/scripts/ScriptLibrary.tsx @@ -15,7 +15,7 @@ * * 风格对齐标题库(.xx-scripts-* 命名,沿用 CSS 变量) */ -import React, { useCallback, useEffect, useState } from "react" +import React, { useCallback, useEffect, useState } from "react"; import { Button, Card, @@ -31,9 +31,10 @@ import { Space, Spin, Tag, + Tooltip, Typography, message, -} from "antd" +} from "antd"; import { PlusOutlined, EditOutlined, @@ -45,8 +46,8 @@ import { VideoCameraOutlined, RobotOutlined, BulbOutlined, -} from "@ant-design/icons" -import { useNavigate } from "react-router-dom" +} from "@ant-design/icons"; +import { useNavigate } from "react-router-dom"; import { getScripts, createScript, @@ -57,119 +58,119 @@ import { aiRewriteScript, aiGenerateTitles, REWRITE_STYLE_OPTIONS, -} from "@/api/scripts" +} from "@/api/scripts"; import type { ScriptItem, ScriptCategory, ScriptUpsertRequest, RewriteStyle, AiRewriteResponse, -} from "@/api/scripts" -import { SCRIPT_CATEGORY_LABEL } from "@/api/scripts" -import "./scripts.css" +} from "@/api/scripts"; +import { SCRIPT_CATEGORY_LABEL } from "@/api/scripts"; +import "./scripts.css"; -const { TextArea } = Input -const { Paragraph, Text } = Typography +const { TextArea } = Input; +const { Paragraph, Text } = Typography; -const PAGE_SIZE = 12 +const PAGE_SIZE = 12; const CATEGORY_OPTIONS: { value: ScriptCategory | "all"; label: string }[] = [ { value: "all", label: "全部分类" }, ...Object.entries(SCRIPT_CATEGORY_LABEL).map(([value, label]) => ({ value: value as ScriptCategory, label, })), -] +]; const FONT_OPTIONS = [ { value: "default", label: "默认" }, { value: "bold", label: "粗体" }, { value: "handwritten", label: "手写" }, { value: "serif", label: "衬线" }, -] +]; const POSITION_OPTIONS = [ { value: "top", label: "顶部" }, { value: "center", label: "居中" }, { value: "bottom", label: "底部" }, -] as const +] as const; /** 提取后端返回的错误 detail(全局拦截器可能已弹 toast,但这里再兜一层) */ function extractErrMsg(err: unknown, fallback: string): string { const e = err as { - response?: { data?: { detail?: string | { message?: string }; message?: string } } - message?: string - } - const data = e?.response?.data + response?: { data?: { detail?: string | { message?: string }; message?: string } }; + message?: string; + }; + const data = e?.response?.data; if (data?.detail) { - if (typeof data.detail === "string") return data.detail - if (typeof data.detail.message === "string") return data.detail.message + if (typeof data.detail === "string") return data.detail; + if (typeof data.detail.message === "string") return data.detail.message; } - if (data?.message && typeof data.message === "string") return data.message - if (e?.message) return e.message - return fallback + if (data?.message && typeof data.message === "string") return data.message; + if (e?.message) return e.message; + return fallback; } const ScriptLibrary: React.FC = () => { - const navigate = useNavigate() + const navigate = useNavigate(); - const [items, setItems] = useState([]) - const [total, setTotal] = useState(0) - const [page, setPage] = useState(1) - const [loading, setLoading] = useState(false) - const [keyword, setKeyword] = useState("") - const [category, setCategory] = useState("all") + const [items, setItems] = useState([]); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [loading, setLoading] = useState(false); + const [keyword, setKeyword] = useState(""); + const [category, setCategory] = useState("all"); // 主弹窗状态 - const [modalOpen, setModalOpen] = useState(false) - const [editing, setEditing] = useState(null) - const [submitting, setSubmitting] = useState(false) - const [form] = Form.useForm() + const [modalOpen, setModalOpen] = useState(false); + const [editing, setEditing] = useState(null); + const [submitting, setSubmitting] = useState(false); + const [form] = Form.useForm(); // ── #1893 AI 能力状态 ── // 抖音提取 - const [douyinModalOpen, setDouyinModalOpen] = useState(false) - const [douyinUrl, setDouyinUrl] = useState("") - const [douyinLoading, setDouyinLoading] = useState(false) + const [douyinModalOpen, setDouyinModalOpen] = useState(false); + const [douyinUrl, setDouyinUrl] = useState(""); + const [douyinLoading, setDouyinLoading] = useState(false); // AI 改写 - const [rewriteModalOpen, setRewriteModalOpen] = useState(false) - const [rewriteStyle, setRewriteStyle] = useState("口语化") - const [rewriteLoading, setRewriteLoading] = useState(false) - const [rewriteResult, setRewriteResult] = useState(null) + const [rewriteModalOpen, setRewriteModalOpen] = useState(false); + const [rewriteStyle, setRewriteStyle] = useState("口语化"); + const [rewriteLoading, setRewriteLoading] = useState(false); + const [rewriteResult, setRewriteResult] = useState(null); // AI 生成标题 - const [titleGenLoading, setTitleGenLoading] = useState(false) - const [titleCandidates, setTitleCandidates] = useState([]) + const [titleGenLoading, setTitleGenLoading] = useState(false); + const [titleCandidates, setTitleCandidates] = useState([]); const load = useCallback(async () => { - setLoading(true) + setLoading(true); try { const res = await getScripts({ page, page_size: PAGE_SIZE, keyword: keyword.trim() || undefined, category, - }) + }); if (Array.isArray(res)) { - setItems(res) - setTotal(res.length) + setItems(res); + setTotal(res.length); } else { - setItems(res.items ?? []) - setTotal(res.total ?? 0) + setItems(res.items ?? []); + setTotal(res.total ?? 0); } } catch (err) { - message.error(extractErrMsg(err, "加载文案列表失败")) + message.error(extractErrMsg(err, "加载文案列表失败")); } finally { - setLoading(false) + setLoading(false); } - }, [page, keyword, category]) + }, [page, keyword, category]); useEffect(() => { - load() - }, [load]) + load(); + }, [load]); const resetCreateForm = () => { - form.resetFields() + form.resetFields(); form.setFieldsValue({ title: "", content: "", @@ -186,19 +187,19 @@ const ScriptLibrary: React.FC = () => { bold: true, italic: false, }, - }) - setRewriteResult(null) - setTitleCandidates([]) - } + }); + setRewriteResult(null); + setTitleCandidates([]); + }; const openCreate = () => { - setEditing(null) - resetCreateForm() - setModalOpen(true) - } + setEditing(null); + resetCreateForm(); + setModalOpen(true); + }; const openEdit = (item: ScriptItem) => { - setEditing(item) + setEditing(item); form.setFieldsValue({ title: item.title, content: item.content, @@ -213,24 +214,24 @@ const ScriptLibrary: React.FC = () => { position: "center", size: 48, }, - }) - setRewriteResult(null) - setTitleCandidates([]) - setModalOpen(true) - } + }); + setRewriteResult(null); + setTitleCandidates([]); + setModalOpen(true); + }; const closeModal = () => { - setModalOpen(false) - setEditing(null) - setRewriteResult(null) - setTitleCandidates([]) - } + setModalOpen(false); + setEditing(null); + setRewriteResult(null); + setTitleCandidates([]); + }; /** 提交新建/编辑 */ const handleSubmit = async () => { try { - const values = await form.validateFields() - setSubmitting(true) + const values = await form.validateFields(); + setSubmitting(true); const payload: ScriptUpsertRequest = { title: values.title.trim(), content: values.content, @@ -239,67 +240,67 @@ const ScriptLibrary: React.FC = () => { title_text: values.title_text?.trim() || undefined, title_category: values.title_category, title_config: values.title_config, - } + }; if (editing) { - await updateScript(editing.id, payload) - message.success("文案已更新") + await updateScript(editing.id, payload); + message.success("文案已更新"); } else { - await createScript(payload) - message.success("文案已创建") + await createScript(payload); + message.success("文案已创建"); } - closeModal() - await load() + closeModal(); + await load(); } catch (err) { - if ((err as { errorFields?: unknown })?.errorFields) return - message.error(extractErrMsg(err, "保存失败")) + if ((err as { errorFields?: unknown })?.errorFields) return; + message.error(extractErrMsg(err, "保存失败")); } finally { - setSubmitting(false) + setSubmitting(false); } - } + }; const handleDelete = async (id: string) => { try { - await deleteScript(id) - message.success("文案已删除") + await deleteScript(id); + message.success("文案已删除"); if (items.length === 1 && page > 1) { - setPage(page - 1) + setPage(page - 1); } else { - await load() + await load(); } } catch (err) { - message.error(extractErrMsg(err, "删除失败")) + message.error(extractErrMsg(err, "删除失败")); } - } + }; const handleDuplicate = async (id: string) => { try { - await duplicateScript(id) - message.success("已复制") - setPage(1) - await load() + await duplicateScript(id); + message.success("已复制"); + setPage(1); + await load(); } catch (err) { - message.error(extractErrMsg(err, "复制失败")) + message.error(extractErrMsg(err, "复制失败")); } - } + }; /** 使用:跳创作页,query 带 scriptId 预填 */ const handleUse = (item: ScriptItem) => { - navigate(`/app/generate?scriptId=${encodeURIComponent(item.id)}`) - } + navigate(`/app/generate?scriptId=${encodeURIComponent(item.id)}`); + }; const preview = (content: string) => { - const text = content.replace(/\s+/g, " ").trim() - if (text.length <= 100) return text || "(空)" - return `${text.slice(0, 100)}…` - } + const text = content.replace(/\s+/g, " ").trim(); + if (text.length <= 100) return text || "(空)"; + return `${text.slice(0, 100)}…`; + }; const formatTime = (iso?: string) => { - if (!iso) return "-" - const d = new Date(iso) - if (Number.isNaN(d.getTime())) return iso - const pad = (n: number) => String(n).padStart(2, "0") - return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}` - } + if (!iso) return "-"; + const d = new Date(iso); + if (Number.isNaN(d.getTime())) return iso; + const pad = (n: number) => String(n).padStart(2, "0"); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; + }; const categoryColor: Record = { promo: "red", @@ -308,36 +309,36 @@ const ScriptLibrary: React.FC = () => { story: "purple", emotion: "magenta", other: "default", - } + }; // ── #1893 AI 操作 ── /** 打开抖音提取弹窗 */ const openDouyinModal = () => { - setDouyinUrl("") - setDouyinModalOpen(true) - } + setDouyinUrl(""); + setDouyinModalOpen(true); + }; /** 执行抖音提取,成功后打开新建弹窗并预填 content */ const handleDouyinExtract = async () => { - const url = douyinUrl.trim() + const url = douyinUrl.trim(); if (!url) { - message.warning("请粘贴抖音视频链接") - return + message.warning("请粘贴抖音视频链接"); + return; } if (!/^https?:\/\//i.test(url)) { - message.warning("请输入以 http(s):// 开头的完整链接") - return + message.warning("请输入以 http(s):// 开头的完整链接"); + return; } - setDouyinLoading(true) + setDouyinLoading(true); try { - const res = await extractScriptFromDouyin({ url }) - message.success(`提取成功${res.duration_seconds ? `(时长 ${res.duration_seconds}s)` : ""}`) - setDouyinModalOpen(false) - setDouyinUrl("") + const res = await extractScriptFromDouyin({ url }); + message.success(`提取成功${res.duration_seconds ? `(时长 ${res.duration_seconds}s)` : ""}`); + setDouyinModalOpen(false); + setDouyinUrl(""); // 关闭抖音弹窗,打开新建弹窗预填 content - setEditing(null) - resetCreateForm() + setEditing(null); + resetCreateForm(); form.setFieldsValue({ title: "", content: res.text, @@ -353,74 +354,74 @@ const ScriptLibrary: React.FC = () => { bold: true, italic: false, }, - }) - setModalOpen(true) + }); + setModalOpen(true); } catch (err) { - message.error(extractErrMsg(err, "抖音文案提取失败")) + message.error(extractErrMsg(err, "抖音文案提取失败")); } finally { - setDouyinLoading(false) + setDouyinLoading(false); } - } + }; /** 执行 AI 改写,结果写入 rewriteResult 让用户对比确认 */ const handleAiRewrite = async () => { - const content = form.getFieldValue("content") as string | undefined + const content = form.getFieldValue("content") as string | undefined; if (!content || !content.trim()) { - message.warning("请先填写文案正文再改写") - return + message.warning("请先填写文案正文再改写"); + return; } - setRewriteLoading(true) - setRewriteResult(null) + setRewriteLoading(true); + setRewriteResult(null); try { - const res = await aiRewriteScript({ content, style: rewriteStyle }) - setRewriteResult(res) + const res = await aiRewriteScript({ content, style: rewriteStyle }); + setRewriteResult(res); } catch (err) { - message.error(extractErrMsg(err, "AI 改写失败")) + message.error(extractErrMsg(err, "AI 改写失败")); } finally { - setRewriteLoading(false) + setRewriteLoading(false); } - } + }; /** 应用改写结果:替换 content 字段,关闭改写弹窗 */ const applyRewrite = () => { - if (!rewriteResult) return - form.setFieldsValue({ content: rewriteResult.rewritten }) - setRewriteResult(null) - setRewriteModalOpen(false) - message.success("已应用改写结果") - } + if (!rewriteResult) return; + form.setFieldsValue({ content: rewriteResult.rewritten }); + setRewriteResult(null); + setRewriteModalOpen(false); + message.success("已应用改写结果"); + }; /** 执行 AI 生成标题,生成候选 */ const handleGenerateTitles = async () => { - const content = form.getFieldValue("content") as string | undefined + const content = form.getFieldValue("content") as string | undefined; if (!content || !content.trim()) { - message.warning("请先填写文案内容") - return + message.warning("请先填写文案内容"); + return; } - setTitleGenLoading(true) - setTitleCandidates([]) + setTitleGenLoading(true); + setTitleCandidates([]); try { - const res = await aiGenerateTitles({ content, count: 3 }) + const res = await aiGenerateTitles({ content, count: 3 }); if (!res.titles || res.titles.length === 0) { - message.info("AI 未返回可用标题,请稍后再试") - return + message.info("AI 未返回可用标题,请稍后再试"); + return; } - setTitleCandidates(res.titles) + setTitleCandidates(res.titles); } catch (err) { - message.error(extractErrMsg(err, "AI 生成标题失败")) + message.error(extractErrMsg(err, "AI 生成标题失败")); } finally { - setTitleGenLoading(false) + setTitleGenLoading(false); } - } + }; /** 点击候选标题直接填入 title 字段 */ const pickTitle = (t: string) => { - form.setFieldsValue({ title: t }) - } + form.setFieldsValue({ title: t }); + }; // 监听 content 字段,用于禁用生成标题按钮(content 为空时) - const watchedContent = Form.useWatch("content", form) - const contentEmpty = !watchedContent || !String(watchedContent).trim() + const watchedContent = Form.useWatch("content", form); + const contentEmpty = !watchedContent || !String(watchedContent).trim(); return (
@@ -433,8 +434,8 @@ const ScriptLibrary: React.FC = () => { placeholder="搜索标题或正文" value={keyword} onChange={(e) => { - setKeyword(e.target.value) - setPage(1) + setKeyword(e.target.value); + setPage(1); }} allowClear style={{ width: 260 }} @@ -442,8 +443,8 @@ const ScriptLibrary: React.FC = () => { { - setCategory(v); - setPage(1); + setCategory(v) + setPage(1) }} style={{ width: 140 }} options={CATEGORY_OPTIONS} @@ -683,8 +683,8 @@ const ScriptLibrary: React.FC = () => { onClick={() => { // 每次打开重置上一次结果,避免误看旧对比 if (!rewriteLoading) { - setRewriteResult(null); - setRewriteModalOpen(true); + setRewriteResult(null) + setRewriteModalOpen(true) } }} > @@ -874,7 +874,7 @@ const ScriptLibrary: React.FC = () => { )}
- ); -}; + ) +} -export default ScriptLibrary; +export default ScriptLibrary -- 2.54.0