From a02232a1c365db4823978af3a3e9979c1b5e2723 Mon Sep 17 00:00:00 2001 From: Coze Agent Date: Sat, 3 Oct 2026 15:21:51 +0800 Subject: [PATCH 1/5] feat(viral-video): inline doc-style storyboard layout - Replace textarea-per-field with click-to-edit inline flow - Labels (field-k) now dark gray (#1f2937) bold, content follows on same line - Time range labels: purple bold text, no pill background - Remove left purple border on shot items - Remove purple left border on voiceover section - Overview split into separate rows (theme/duration/aspect) - Voiceover section uses same field-val pattern - Keep Select for aspect ratio inline - tsc --noEmit: 0 errors, vite build: success --- .../src/pages/viral-video/ViralVideoPage.css | 152 +++---- .../src/pages/viral-video/ViralVideoPage.tsx | 384 ++++++++++++++---- 2 files changed, 363 insertions(+), 173 deletions(-) diff --git a/apps/web/src/pages/viral-video/ViralVideoPage.css b/apps/web/src/pages/viral-video/ViralVideoPage.css index c2a770d1a..6dc6ece13 100644 --- a/apps/web/src/pages/viral-video/ViralVideoPage.css +++ b/apps/web/src/pages/viral-video/ViralVideoPage.css @@ -1103,53 +1103,15 @@ margin-top: 0; } -/* 总览 —— 单行段落 */ -.vv-sb-overview { +/* 总览 —— 每行一段 */ +.vv-sb-inline-row { + display: flex; + align-items: baseline; + flex-wrap: wrap; font-size: 13px; line-height: 1.7; color: #1f2937; - margin: 4px 0 6px; - padding: 4px 8px; - background: #fafafe; - border-radius: 6px; -} -.vv-sb-overview strong { - color: #374151; - font-weight: 600; -} -.vv-sb-overview .vv-sb-inline-text, -.vv-sb-overview .ant-select { - vertical-align: middle; -} -.vv-sb-inline-text { - background: transparent; - border: none; - color: #1f2937; - font: inherit; - padding: 1px 4px; - outline: none; - border-radius: 4px; - border-bottom: 1px dashed transparent; - transition: - border-color 0.15s, - background 0.15s; -} -.vv-sb-inline-text:hover, -.vv-sb-inline-text:focus { - background: #f5f0ff; - border-bottom-color: #7c3aed; -} -.vv-sb-inline-text:disabled { - color: #9ca3af; - cursor: default; -} -.vv-sb-inline-text-sm { - width: auto; - max-width: 80px; -} -.vv-sb-sep { - color: #d1d5db; - margin: 0 8px; + margin: 2px 0; } .vv-sb-inline-select { @@ -1169,7 +1131,7 @@ padding-left: 0 !important; } -/* 段落式 textarea 基础样式 */ +/* 段落式 textarea 基础样式(仅编辑态使用) */ .vv-sb-doc-ta { background: transparent !important; border: 1px dashed transparent !important; @@ -1186,21 +1148,36 @@ border-color: #7c3aed !important; background: #f5f0ff !important; } -.vv-sb-doc-ta-sm { - min-height: 24px; -} /* 场景与光线 —— 段落样式 */ .vv-sb-para { margin: 4px 0; } -.vv-sb-doc-ta-block { + +/* 内联编辑 textarea(点击后弹出) */ +.vv-sb-inline-edit-ta { display: block; width: 100%; - padding: 4px 8px !important; + margin-top: 4px; + min-height: 28px; + background: #fafafe !important; + border: 1px solid #d8cafc !important; + border-radius: 6px !important; + padding: 6px 8px !important; font-size: 13px !important; line-height: 1.6 !important; - min-height: 32px; + font-family: inherit; + color: #1f2937 !important; + outline: none; + resize: vertical; +} +.vv-sb-inline-edit-ta-sm { + max-width: 120px; +} +.vv-sb-time-ta { + max-width: 160px; + font-weight: 600; + color: #7c3aed !important; } /* 逐镜头 */ @@ -1211,49 +1188,51 @@ margin-top: 2px; } .vv-sb-doc-shot { - padding-left: 8px; - border-left: 2px solid rgba(124, 58, 237, 0.3); display: flex; flex-direction: column; gap: 2px; + margin-bottom: 6px; } .vv-sb-time-doc { - display: inline-block; - font-size: 13px; - font-weight: 700; + display: block; + font-size: 14px; + font-weight: 600; color: #7c3aed; - background: rgba(124, 58, 237, 0.08); - border: none; - outline: none; - padding: 1px 6px; - font-family: inherit; - border-radius: 4px; - margin-bottom: 4px; + margin: 6px 0 2px; + cursor: text; } -.vv-sb-time-doc:focus { - background: #f5f0ff; +.vv-sb-time-doc:hover { + background: rgba(124, 58, 237, 0.06); + border-radius: 3px; } /* 字段段落 */ .vv-sb-field { - display: block; + display: flex; + flex-wrap: wrap; + align-items: baseline; margin: 2px 0; font-size: 13px; - line-height: 1.65; + line-height: 1.6; } .vv-sb-field-k { - color: #6d28d9; + color: #1f2937; font-weight: 600; - margin-right: 4px; + margin-right: 0; + white-space: nowrap; } -.vv-sb-doc-ta-inline { - display: block; - width: 100%; - margin-top: 1px; - padding: 2px 4px !important; - font-size: 13px !important; - line-height: 1.6 !important; - min-height: 24px; +.vv-sb-field-val { + color: #374151; + cursor: text; + border-radius: 3px; + padding: 0 2px; + transition: background 0.15s; + word-break: break-word; + flex: 1; + min-width: 0; +} +.vv-sb-field:hover .vv-sb-field-val { + background: rgba(124, 58, 237, 0.06); } /* 参考图片行 */ @@ -1396,24 +1375,7 @@ background: #f5f0ff; } -/* 口播稿 */ -.vv-sb-vo-wrap { - margin-top: 4px; -} -.vv-sb-vo { - display: block; - width: 100%; - background: #fafafe !important; - border-left: 3px solid #7c3aed !important; - border-radius: 0 6px 6px 0 !important; - padding: 6px 10px !important; - min-height: 44px; - font-size: 13px !important; - line-height: 1.7 !important; - color: #1f2937 !important; - border: none !important; - resize: vertical; -} +/* 口播稿 —— 复用 vv-sb-field 样式,无额外需求 */ .vv-sb-actions { display: flex; diff --git a/apps/web/src/pages/viral-video/ViralVideoPage.tsx b/apps/web/src/pages/viral-video/ViralVideoPage.tsx index 4f31cccea..7a760c33b 100644 --- a/apps/web/src/pages/viral-video/ViralVideoPage.tsx +++ b/apps/web/src/pages/viral-video/ViralVideoPage.tsx @@ -1163,6 +1163,9 @@ const ViralVideoPage: React.FC = () => { } /* ── 分镜脚本结果区(编导分镜卡片 UI) ── */ + const [editingField, setEditingField] = useState(null) + const editTaRef = useRef(null) + const updateShot = (idx: number, patch: Partial) => { if (!task.storyboard) return const shots = task.storyboard.shots.map((s, i) => (i === idx ? { ...s, ...patch } : s)) @@ -1198,6 +1201,30 @@ const ViralVideoPage: React.FC = () => { setTask({ storyboard: { ...task.storyboard, [list]: arr } }) } + /* 内联编辑 helper —— 点击文本 → textarea,blur/Enter 保存 */ + const handleInlineCommit = useCallback(() => { + const ta = editTaRef.current + if (!ta) return + const key = ta.dataset.field + if (key) { + const val = ta.value + if (key.startsWith("shot.")) { + const parts = key.split(".") + const idx = Number(parts[1]) + const field = parts.slice(2).join(".") + updateShot(idx, { [field]: val } as Partial) + } else if (key.startsWith("overview.")) { + const field = key.split(".")[1] + updateOverview({ [field]: val } as Partial) + } else if (key === "scene_and_lighting") { + updateStoryboard({ scene_and_lighting: val }) + } else if (key === "voiceover_script") { + updateStoryboard({ voiceover_script: val }) + } + } + setEditingField(null) + }, []) + const renderCopyResult = () => { if (task.uiStep === "step2_generating") { return ( @@ -1253,27 +1280,69 @@ const ViralVideoPage: React.FC = () => {
{/* 视频总览 */}

视频总览

-

- 整体主题: - updateOverview({ theme: e.target.value })} - /> - · - 总时长: - updateOverview({ total_duration: e.target.value })} - /> - · - 画幅: +

+ 整体主题: + {editingField === "overview.theme" ? ( +