优化住流程和添加复刻视频页面

This commit is contained in:
Azmat@qq.com
2026-08-26 18:44:03 +08:00
parent 0ee498d807
commit c25060c6c6
30 changed files with 2574 additions and 155 deletions
+19 -13
View File
@@ -1450,10 +1450,9 @@ export function PipelinePage(props: {
const recommended = useMemo(
() => recommendSetup({
category: setupProduct?.category,
targetAudience: setupProduct?.target_audience,
title: setupProduct?.title,
}),
[setupProduct?.category, setupProduct?.target_audience, setupProduct?.title]
[setupProduct?.category, setupProduct?.title]
);
const wizard = project.metadata?.wizard;
const [setupOpen, setSetupOpen] = useState(false);
@@ -1464,7 +1463,9 @@ export function PipelinePage(props: {
const [setupStructure, setSetupStructure] = useState<VideoStructure>(
coerceVideoStructure(wizard?.video_structure, recommended.structure)
);
const [setupPersona, setSetupPersona] = useState<string>(coercePersona(wizard?.persona, SETUP_PERSONA_KEYS[0] || "urban"));
const [setupPersona, setSetupPersona] = useState<string>(
coercePersona(wizard?.persona, recommended.persona || SETUP_PERSONA_KEYS[0] || "urban")
);
const [setupDuration, setSetupDuration] = useState<number>(() => {
if (typeof wizard?.total_duration === "number") return clampDuration(wizard.total_duration);
// 兼容一期向导存的字符串档位("0-30" → 30)
@@ -1476,12 +1477,16 @@ export function PipelinePage(props: {
const setupTouched = useRef(false);
const wizardHasCombo = Boolean(wizard?.presentation_format && wizard?.video_structure);
const wizardHasDuration = typeof wizard?.total_duration === "number" || Boolean(wizard?.duration);
const wizardHasPersona = Boolean(wizard?.persona);
useEffect(() => {
if (setupTouched.current || wizardHasCombo) return;
setSetupFormat(recommended.format);
setSetupStructure(recommended.structure);
if (!wizardHasDuration) setSetupDuration(recommended.duration);
}, [recommended.format, recommended.structure, recommended.duration, wizardHasCombo, wizardHasDuration]);
if (setupTouched.current) return;
if (!wizardHasCombo) {
setSetupFormat(recommended.format);
setSetupStructure(recommended.structure);
if (!wizardHasDuration) setSetupDuration(recommended.duration);
}
if (!wizardHasPersona) setSetupPersona(recommended.persona);
}, [recommended.format, recommended.structure, recommended.persona, recommended.duration, wizardHasCombo, wizardHasDuration, wizardHasPersona]);
// ── 5.2 换商品重跑 ── 新建向导选的套路模板由后端回填进 metadata.wizard,这里只读不写
const templateName = typeof wizard?.template_name === "string" ? wizard.template_name : "";
@@ -1875,7 +1880,7 @@ export function PipelinePage(props: {
`${base}\n以上是我拆解一条参考视频得到的分镜稿。请照搬它的镜头顺序、每镜时长比例、景别、机位、运镜、人物动作和声音层次,`
+ `把人物、商品、品牌、台词全部换成我自己的商品;参考视频里出现的商品、品牌、人物一律不要保留。`
+ `写 visual 时把每镜的景别、机位、运镜、动作、表情、音效、背景音乐、字幕、备注折进导演说明书,不要压成一句画面摘要。`
+ `原稿里标注「无 / 不可见 / 听不清 / 存疑」的地方不要编造,由你按我的商品补全。目标人群:${personaLabel}`,
+ `原稿里标注「无 / 不可见 / 听不清 / 存疑」的地方不要编造,由你按我的商品补全。人物设定:${personaLabel}`,
`上传视频提炼 · ${combo}`,
"video",
);
@@ -1891,7 +1896,7 @@ export function PipelinePage(props: {
// 上传脚本:先识别原稿结构,再补齐钩子 / 卖点证明 / 转化,而不是照抄一遍
await runScriptGeneration(
`${base}\n以上是我已有的脚本。请先识别它的结构与叙述顺序,尽量保留原意与原有表达,再据此整理成镜头脚本;`
+ `其中开头钩子、卖点的证明方式、结尾转化引导如有缺失或偏弱,请补齐补强。目标人群:${personaLabel}`,
+ `其中开头钩子、卖点的证明方式、结尾转化引导如有缺失或偏弱,请补齐补强。人物设定:${personaLabel}`,
`上传脚本 · ${combo}`,
"manual",
);
@@ -3326,11 +3331,11 @@ export function PipelinePage(props: {
<div className="setup-rec">{STRUCTURE_HINT[setupStructure]}</div>
<label className="setup-field">
<span className="sf-k">人物</span>
<select className="setup-select" value={setupPersona} onChange={(e) => setSetupPersona(e.target.value)}>
<select className="setup-select" value={setupPersona} onChange={(e) => { setupTouched.current = true; setSetupPersona(e.target.value); }}>
{SETUP_PERSONA_KEYS.map((k) => <option key={k} value={k}>{WIZ_PERSONA_LABEL[k]}</option>)}
</select>
</label>
<div className="setup-rec">根据商品目标人群推荐</div>
<div className="setup-rec">{recommended.reason} · {WIZ_PERSONA_LABEL[recommended.persona] || recommended.persona}</div>
{/* 时长:15/30/45/60;每镜固定 15 秒 */}
<label className="setup-field">
<span className="sf-k">时长</span>
@@ -3343,10 +3348,11 @@ export function PipelinePage(props: {
{/* ← 返回:收起设定卡,回到「脚本辅助生成 / 上传脚本」入口页 */}
<button type="button" className="btn btn-ghost btn-sm" onClick={() => setSetupOpen(false)}>← 返回</button>
<button type="button" className="btn btn-ghost btn-sm" title={recommended.reason} onClick={() => {
// 重新推荐:回到按商品信息算出的那一组(不是随机换,随机换等于没推荐)
// 重新推荐:回到按商品品类算出的那一组(不是随机换,随机换等于没推荐)
setupTouched.current = false;
setSetupFormat(recommended.format);
setSetupStructure(recommended.structure);
setSetupPersona(recommended.persona);
setSetupDuration(recommended.duration);
}}>重新推荐</button>
<button type="button" className="btn btn-primary btn-sm" disabled={loading || scriptFileBusy || videoDigestBusy || ((setupSource === "manual" || setupSource === "video") && !chatText.trim())} onClick={() => void runScriptWithSetup()}>确定</button>