fix(viral-video): STEP2 UI 重做 - 2列表单/删BGM/文案区重设按钮流 (#2124)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 15s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 7s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 38s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 25s
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m37s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m56s
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Validate - Style (push) Has been cancelled
CI/CD Pipeline / Validate - Security (push) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 15s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 7s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 38s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 25s
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m37s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m56s
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Validate - Style (push) Has been cancelled
CI/CD Pipeline / Validate - Security (push) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
This commit was merged in pull request #2124.
This commit is contained in:
@@ -1596,3 +1596,108 @@
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── STEP2 2列表单网格 ── */
|
||||
.vv-form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 14px 18px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.vv-form-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
.vv-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.vv-label-optional {
|
||||
font-size: 12px;
|
||||
color: #9ca3af;
|
||||
font-weight: 400;
|
||||
}
|
||||
.vv-input {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e5e7eb;
|
||||
background: #fff;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
color: #111827;
|
||||
outline: none;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
box-shadow 0.15s;
|
||||
}
|
||||
.vv-input:focus {
|
||||
border-color: #7c3aed;
|
||||
box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15);
|
||||
}
|
||||
.vv-input:disabled {
|
||||
background: #f9fafb;
|
||||
color: #9ca3af;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.vv-input::placeholder {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
/* 自定义 Select 样式(浅色紫调) */
|
||||
.vv-select-purple .ant-select-selector {
|
||||
border-radius: 8px !important;
|
||||
border: 1px solid #e5e7eb !important;
|
||||
background: #fff !important;
|
||||
min-height: 36px !important;
|
||||
padding: 0 28px 0 12px !important;
|
||||
box-shadow: none !important;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
box-shadow 0.15s !important;
|
||||
}
|
||||
.vv-select-purple.ant-select-focused .ant-select-selector,
|
||||
.vv-select-purple .ant-select-focused .ant-select-selector {
|
||||
border-color: #7c3aed !important;
|
||||
box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.15) !important;
|
||||
}
|
||||
.vv-select-purple .ant-select-selection-item,
|
||||
.vv-select-purple .ant-select-selection-placeholder {
|
||||
line-height: 34px !important;
|
||||
font-size: 13px !important;
|
||||
color: #111827 !important;
|
||||
}
|
||||
.vv-select-purple .ant-select-selection-placeholder {
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
.vv-select-purple .ant-select-arrow {
|
||||
color: #6b7280;
|
||||
right: 10px !important;
|
||||
}
|
||||
.vv-select-purple.ant-select-disabled .ant-select-selector {
|
||||
background: #f9fafb !important;
|
||||
color: #9ca3af !important;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ghost/outline 按钮 */
|
||||
.vv-btn-ghost {
|
||||
background: #fff;
|
||||
color: #7c3aed;
|
||||
border: 1px solid #d6c6fb;
|
||||
}
|
||||
.vv-btn-ghost:hover {
|
||||
background: #f8f4ff;
|
||||
border-color: #7c3aed;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.vv-form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,11 +109,11 @@ type TabTask = {
|
||||
userCopy: string
|
||||
industry: string
|
||||
targetCustomer: string
|
||||
persona: string
|
||||
language: string
|
||||
viralStructure: string
|
||||
marketingPurpose: string
|
||||
duration: number
|
||||
bgm: string
|
||||
// STEP3 设置
|
||||
videoRatio: string
|
||||
videoModel: string
|
||||
@@ -134,17 +134,18 @@ type TabTask = {
|
||||
|
||||
/* ─────────── 常量 ─────────── */
|
||||
|
||||
const INDUSTRIES = [
|
||||
"美妆个护",
|
||||
"服饰鞋包",
|
||||
"数码3C",
|
||||
"食品饮料",
|
||||
"家居日用",
|
||||
"母婴亲子",
|
||||
"教育培训",
|
||||
"其他",
|
||||
]
|
||||
const LANGUAGES = ["中文(普通话)", "粤语", "英语", "日语", "韩语"]
|
||||
const TARGET_CUSTOMERS = [
|
||||
"小镇青年",
|
||||
"小镇中老年",
|
||||
"Z世代",
|
||||
"精致妈妈",
|
||||
"新锐白领",
|
||||
"资深中产",
|
||||
"都市蓝领",
|
||||
"都市银发",
|
||||
]
|
||||
const PERSONAS = ["亲切邻家姐姐", "专业顾问", "搞笑段子手", "真实测评博主", "闺蜜种草", "行业专家"]
|
||||
const STRUCTURES = [
|
||||
"痛点开场→卖点→促单",
|
||||
"反差对比→证据→促单",
|
||||
@@ -152,7 +153,7 @@ const STRUCTURES = [
|
||||
"专家背书→数据→促单",
|
||||
]
|
||||
const PURPOSES = ["品牌种草", "新品上市", "促销转化", "活动引流"]
|
||||
const BGMS = ["无BGM", "欢快活泼", "温柔治愈", "励志燃向", "搞笑魔性", "科技感"]
|
||||
const DURATIONS = [15, 20, 30, 45, 60]
|
||||
const RATIOS = [
|
||||
{ v: "9:16", label: "9:16 竖屏(抖音/视频号)" },
|
||||
{ v: "16:9", label: "16:9 横屏(B站/YouTube)" },
|
||||
@@ -187,8 +188,8 @@ const emptyTask = (id: string, title: string): TabTask => ({
|
||||
language: "中文(普通话)",
|
||||
viralStructure: STRUCTURES[0],
|
||||
marketingPurpose: PURPOSES[0],
|
||||
duration: 30,
|
||||
bgm: BGMS[0],
|
||||
duration: 15,
|
||||
persona: "",
|
||||
videoRatio: "9:16",
|
||||
videoModel: "seedance-2.5",
|
||||
uiStep: "step1_upload",
|
||||
@@ -655,11 +656,10 @@ const ViralVideoPage: React.FC = () => {
|
||||
const job = await generateViralCopy(task.jobId, {
|
||||
industry: task.industry || undefined,
|
||||
target_customer: task.targetCustomer || undefined,
|
||||
persona_id: undefined,
|
||||
viral_structure: task.viralStructure || undefined,
|
||||
marketing_purpose: task.marketingPurpose || undefined,
|
||||
bgm_preference: task.bgm === "无BGM" ? undefined : task.bgm,
|
||||
duration: task.duration,
|
||||
persona_id: task.persona || undefined,
|
||||
user_copy_text:
|
||||
task.fusionLevel === "user_primary" && task.userCopy ? task.userCopy : undefined,
|
||||
fusion_level: task.fusionLevel,
|
||||
@@ -673,33 +673,6 @@ const ViralVideoPage: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── STEP 2:确认文案 → 直接触发 STEP 3 视频生成(confirm-copy 接口) ── */
|
||||
const handleConfirmCopy = async () => {
|
||||
if (!task.generatedCopy.trim()) {
|
||||
message.warning("文案不能为空")
|
||||
return
|
||||
}
|
||||
if (!task.jobId) {
|
||||
message.warning("请先完成文案生成")
|
||||
return
|
||||
}
|
||||
setTask({ uiStep: "step3_generating", videoError: undefined })
|
||||
try {
|
||||
const edited = task.generatedCopy.trim()
|
||||
const originalCopy = task.job?.copy_result?.final_copy || task.generatedCopy
|
||||
const job = await confirmViralCopy(task.jobId, {
|
||||
// 若用户未修改则不传 edited_copy,后端用 AI 文案;若修改过则传编辑后文案
|
||||
edited_copy: edited !== originalCopy.trim() ? edited : undefined,
|
||||
})
|
||||
setTask({ job })
|
||||
// 轮询继续,status=completed 时推进 step3_done
|
||||
message.success("已提交视频生成,预计 1-3 分钟…")
|
||||
} catch (err: unknown) {
|
||||
message.error(err instanceof Error ? err.message : "提交失败")
|
||||
setTask({ uiStep: "failed", videoError: err instanceof Error ? err.message : "提交失败" })
|
||||
}
|
||||
}
|
||||
|
||||
/* ── STEP 3:开始生成视频(重试/兜底入口,正常流程由 handleConfirmCopy 触发) ──
|
||||
* 三步拆分后端下,点击「确认文案并生成视频」会直接走 confirmViralCopy 触发 TTS+渲染。
|
||||
* 此处保留「开始生成视频」按钮作为网络中断/失败后重试、或旧状态兼容入口:
|
||||
@@ -711,8 +684,10 @@ const ViralVideoPage: React.FC = () => {
|
||||
let job
|
||||
if (task.jobId && task.generatedCopy.trim()) {
|
||||
// 新三步链路:基于已有 job 触发 confirm-copy
|
||||
const edited = task.generatedCopy.trim()
|
||||
const originalCopy = task.job?.copy_result?.final_copy || ""
|
||||
job = await confirmViralCopy(task.jobId, {
|
||||
edited_copy: task.generatedCopy.trim() || undefined,
|
||||
edited_copy: edited && edited !== originalCopy.trim() ? edited : undefined,
|
||||
})
|
||||
} else {
|
||||
// 兜底:走旧 /generate 接口(一次性跑完)
|
||||
@@ -727,7 +702,6 @@ const ViralVideoPage: React.FC = () => {
|
||||
fusion_level: task.fusionLevel,
|
||||
voice_id: task.refAudio?.id || undefined,
|
||||
voice_source: task.refAudio?.source,
|
||||
bgm_preference: task.bgm === "无BGM" ? undefined : task.bgm,
|
||||
industry: task.industry || undefined,
|
||||
target_customer: task.targetCustomer || undefined,
|
||||
language: task.language || undefined,
|
||||
@@ -800,6 +774,7 @@ const ViralVideoPage: React.FC = () => {
|
||||
task.uiStep === "step3_done" ||
|
||||
task.uiStep === "failed"
|
||||
const step3Enabled =
|
||||
task.uiStep === "step2_copy_ready" ||
|
||||
task.uiStep === "step3_ready" ||
|
||||
task.uiStep === "step3_generating" ||
|
||||
task.uiStep === "step3_done" ||
|
||||
@@ -876,25 +851,29 @@ const ViralVideoPage: React.FC = () => {
|
||||
task.uiStep === "step3_generating" ||
|
||||
task.uiStep === "step3_done"
|
||||
) {
|
||||
const locked = task.uiStep !== "step2_copy_ready"
|
||||
// 文案始终可编辑(即便是视频生成中也可以看但锁编辑)
|
||||
const locked = task.uiStep === "step3_generating" || task.uiStep === "step3_done"
|
||||
return (
|
||||
<div className="vv-copy-box">
|
||||
<div className="vv-copy-head">
|
||||
<EditOutlined style={{ color: "#7c3aed" }} />
|
||||
<span className="vv-copy-title">AI 生成文案</span>
|
||||
{task.generatedTitle && <span className="vv-copy-sub">{task.generatedTitle}</span>}
|
||||
{locked && <span className="vv-copy-locked">(已锁定)</span>}
|
||||
</div>
|
||||
<textarea
|
||||
className="vv-textarea vv-copy-textarea"
|
||||
value={task.generatedCopy}
|
||||
onChange={(e) => setTask({ generatedCopy: e.target.value })}
|
||||
disabled={locked}
|
||||
placeholder="AI 生成的营销文案将显示在这里…"
|
||||
placeholder="AI 生成的营销文案将显示在这里,可直接编辑修改…"
|
||||
/>
|
||||
{task.uiStep === "step2_copy_ready" && (
|
||||
<button className="vv-btn vv-btn-primary vv-btn-block" onClick={handleConfirmCopy}>
|
||||
<VideoCameraOutlined /> 确认文案并生成视频
|
||||
<button
|
||||
className="vv-btn vv-btn-ghost vv-btn-block"
|
||||
onClick={handleGenerateCopy}
|
||||
style={{ marginTop: 8 }}
|
||||
>
|
||||
<ReloadOutlined /> 重新生成文案
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -1298,7 +1277,7 @@ const ViralVideoPage: React.FC = () => {
|
||||
<div className="vv-section">
|
||||
<div className="vv-step-subhead">
|
||||
<span className={`vv-step-badge ${step2Enabled ? "" : "vv-step-badge-dim"}`}>2</span>
|
||||
<span className="vv-step-subtitle">STEP 2 文案配置并生成文案</span>
|
||||
<span className="vv-step-subtitle">STEP 2 生成视频文案</span>
|
||||
{!step2Enabled && <span className="vv-step-lock">请先完成 STEP 1</span>}
|
||||
</div>
|
||||
<div className="vv-section-body">
|
||||
@@ -1330,38 +1309,37 @@ const ViralVideoPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 视频参数 */}
|
||||
<div className="vv-section-subtitle" style={{ marginTop: 16 }}>
|
||||
视频参数
|
||||
</div>
|
||||
{/* 文案参数(2列网格) */}
|
||||
<div className="vv-form-grid">
|
||||
<div className="vv-form-row">
|
||||
<label className="vv-label">行业</label>
|
||||
<label className="vv-label">
|
||||
行业与产品 <span className="vv-label-optional">(选填)</span>
|
||||
</label>
|
||||
<input
|
||||
className="vv-input"
|
||||
placeholder="如:家具店、宠物店、装修公司"
|
||||
value={task.industry}
|
||||
onChange={(e) => setTask({ industry: e.target.value })}
|
||||
disabled={!canEditStep2 || !step2Enabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="vv-form-row">
|
||||
<label className="vv-label">目标客户</label>
|
||||
<Select
|
||||
className="vv-select"
|
||||
className="vv-select vv-select-purple"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="请选择"
|
||||
value={task.industry || undefined}
|
||||
onChange={(v) => setTask({ industry: v })}
|
||||
options={INDUSTRIES.map((i) => ({ value: i, label: i }))}
|
||||
placeholder="请选择目标客户"
|
||||
value={task.targetCustomer || undefined}
|
||||
onChange={(v) => setTask({ targetCustomer: v })}
|
||||
options={TARGET_CUSTOMERS.map((i) => ({ value: i, label: i }))}
|
||||
allowClear
|
||||
disabled={!canEditStep2 || !step2Enabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="vv-form-row">
|
||||
<label className="vv-label">目标人群</label>
|
||||
<input
|
||||
className="vv-input"
|
||||
placeholder="例:25-35岁职场女性"
|
||||
value={task.targetCustomer}
|
||||
onChange={(e) => setTask({ targetCustomer: e.target.value })}
|
||||
disabled={!canEditStep2 || !step2Enabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="vv-form-row">
|
||||
<label className="vv-label">语言</label>
|
||||
<label className="vv-label">台词语言</label>
|
||||
<Select
|
||||
className="vv-select"
|
||||
className="vv-select vv-select-purple"
|
||||
style={{ width: "100%" }}
|
||||
value={task.language}
|
||||
onChange={(v) => setTask({ language: v })}
|
||||
@@ -1370,20 +1348,22 @@ const ViralVideoPage: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="vv-form-row">
|
||||
<label className="vv-label">视频时长</label>
|
||||
<label className="vv-label">我的人设</label>
|
||||
<Select
|
||||
className="vv-select"
|
||||
className="vv-select vv-select-purple"
|
||||
style={{ width: "100%" }}
|
||||
value={task.duration}
|
||||
onChange={(v) => setTask({ duration: v })}
|
||||
options={[15, 20, 30, 45, 60].map((n) => ({ value: n, label: `${n}秒` }))}
|
||||
placeholder="请选择人设"
|
||||
value={task.persona || undefined}
|
||||
onChange={(v) => setTask({ persona: v })}
|
||||
options={PERSONAS.map((i) => ({ value: i, label: i }))}
|
||||
allowClear
|
||||
disabled={!canEditStep2 || !step2Enabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="vv-form-row">
|
||||
<label className="vv-label">叙事结构</label>
|
||||
<label className="vv-label">爆款结构</label>
|
||||
<Select
|
||||
className="vv-select"
|
||||
className="vv-select vv-select-purple"
|
||||
style={{ width: "100%" }}
|
||||
value={task.viralStructure}
|
||||
onChange={(v) => setTask({ viralStructure: v })}
|
||||
@@ -1394,7 +1374,7 @@ const ViralVideoPage: React.FC = () => {
|
||||
<div className="vv-form-row">
|
||||
<label className="vv-label">营销目的</label>
|
||||
<Select
|
||||
className="vv-select"
|
||||
className="vv-select vv-select-purple"
|
||||
style={{ width: "100%" }}
|
||||
value={task.marketingPurpose}
|
||||
onChange={(v) => setTask({ marketingPurpose: v })}
|
||||
@@ -1403,19 +1383,15 @@ const ViralVideoPage: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="vv-form-row" style={{ gridColumn: "1 / -1" }}>
|
||||
<label className="vv-label">BGM 风格</label>
|
||||
<div className="vv-pill-row">
|
||||
{BGMS.map((b) => (
|
||||
<button
|
||||
key={b}
|
||||
className={`vv-pill ${task.bgm === b ? "active" : ""}`}
|
||||
onClick={() => setTask({ bgm: b })}
|
||||
disabled={!canEditStep2 || !step2Enabled}
|
||||
>
|
||||
{b}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<label className="vv-label">文案视频时长</label>
|
||||
<Select
|
||||
className="vv-select vv-select-purple"
|
||||
style={{ width: "100%" }}
|
||||
value={task.duration}
|
||||
onChange={(v) => setTask({ duration: v })}
|
||||
options={DURATIONS.map((n) => ({ value: n, label: `${n}秒` }))}
|
||||
disabled={!canEditStep2 || !step2Enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1575,13 +1551,7 @@ const ViralVideoPage: React.FC = () => {
|
||||
className={`vv-btn vv-btn-primary ${!step3Enabled ? "vv-btn-disabled-gray" : ""}`}
|
||||
onClick={handleGenerateVideo}
|
||||
disabled={task.uiStep === "step3_generating" || !step3Enabled}
|
||||
title={
|
||||
!step3Enabled
|
||||
? !step2Enabled
|
||||
? "请先完成图片分析"
|
||||
: "请先在 STEP 2 生成并确认文案"
|
||||
: ""
|
||||
}
|
||||
title={!step3Enabled ? "请先在 STEP 2 生成文案" : ""}
|
||||
>
|
||||
{task.uiStep === "step3_generating" ? (
|
||||
<>
|
||||
@@ -1589,8 +1559,7 @@ const ViralVideoPage: React.FC = () => {
|
||||
</>
|
||||
) : !step3Enabled ? (
|
||||
<>
|
||||
<VideoCameraOutlined /> 开始生成视频
|
||||
{!step2Enabled ? "(请先完成图片分析)" : "(请先确认文案)"}
|
||||
<VideoCameraOutlined /> 开始生成视频(请先生成文案)
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user