Files
xiaoxia-saas/apps/web/src/pages/generate/components/Step1EditMode.tsx
T
xiaoxia 34ffe14aae
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
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 / Check push changed paths (push) Successful in 2s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 49s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 49s
CI/CD Pipeline / Build Staging API Image (push) Successful in 57s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 51s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m37s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m41s
CI/CD Pipeline / Integration Tests (push) Successful in 3m52s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m2s
CI/CD Pipeline / Validate - Style (push) Successful in 4m17s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m27s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m32s
CI/CD Pipeline / Validate - Security (push) Successful in 8m22s
CI/CD Pipeline / Unit Tests (push) Successful in 10m32s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 57s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m30s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m11s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 13s
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 23s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 51s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 17s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m7s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Successful in 7m14s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m24s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 7m30s
CI/CD Pipeline / Unit Tests (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 / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (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 / Validate - Security (pull_request) Has been cancelled
fix(generate): #1970 Step1 智能降重开关紧贴标题文字 (#1977)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-18 10:39:59 +08:00

265 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Step 1 选择剪辑模式 + 生成设置(#1970 新流程第一步)
* - 剪辑模式:🎲随机混剪 / 📖叙事剪辑,二选一,选中紫底紫框
* - 生成设置:生成数量(-/+ 1-10 默认1)、视频比例(9:16/16:9 默认9:16)、智能降重开关(默认开)
*/
import React from "react"
import { MinusOutlined, PlusOutlined } from "@ant-design/icons"
export type EditMode = "random" | "narrative"
interface Step1EditModeProps {
editMode: EditMode
onEditModeChange: (mode: EditMode) => void
/** 生成数量(1-10,默认1) */
previewCount: number
onPreviewCountChange: (n: number) => void
/** 视频比例 */
videoRatio: "9:16" | "16:9"
onVideoRatioChange: (ratio: "9:16" | "16:9") => void
/** 智能降重开关(默认 true) */
dedupEnabled: boolean
onDedupEnabledChange: (v: boolean) => void
}
const PURPLE = "#7c3aed"
const PURPLE_BG = "linear-gradient(135deg, #ede9fe, #ddd6fe)"
const PURPLE_BORDER = "2px solid #7c3aed"
const MODE_CARDS: Array<{
key: EditMode
emoji: string
title: string
desc: string
features: string[]
}> = [
{
key: "random",
emoji: "🎲",
title: "随机混剪",
desc: "根据配音时长随机抽取素材片段,灵活组合",
features: ["随机抽帧组合", "每次画面不同", "适合批量生成"],
},
{
key: "narrative",
emoji: "📖",
title: "叙事剪辑",
desc: "按文案内容匹配相关画面,有逻辑组织镜头",
features: ["画面匹配文案", "叙事感更强", "需要素材标签"],
},
]
const Step1EditMode: React.FC<Step1EditModeProps> = ({
editMode,
onEditModeChange,
previewCount,
onPreviewCountChange,
videoRatio,
onVideoRatioChange,
dedupEnabled,
onDedupEnabledChange,
}) => {
return (
<div className="xx-form-section">
<h3>🎬 选择剪辑模式</h3>
<p style={{ color: "#666", fontSize: 14, marginBottom: 16 }}>
选择适合您的剪辑方式,后续流程会根据模式自动调整
</p>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
gap: 16,
marginBottom: 24,
}}
>
{MODE_CARDS.map((card) => {
const selected = editMode === card.key
return (
<div
key={card.key}
onClick={() => onEditModeChange(card.key)}
style={{
padding: 20,
borderRadius: 12,
border: selected ? PURPLE_BORDER : "1px solid #e5e7eb",
background: selected ? PURPLE_BG : "#fff",
cursor: "pointer",
transition: "all 0.2s",
}}
>
<div style={{ fontSize: 36, marginBottom: 8 }}>{card.emoji}</div>
<div
style={{
fontSize: 18,
fontWeight: 600,
color: selected ? PURPLE : "#111",
marginBottom: 6,
}}
>
{card.title}
</div>
<div style={{ fontSize: 13, color: "#666", marginBottom: 12 }}>{card.desc}</div>
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
{card.features.map((f) => (
<div key={f} style={{ fontSize: 12, color: selected ? "#6d28d9" : "#6b7280" }}>
✅ {f}
</div>
))}
</div>
</div>
)
})}
</div>
<h3 style={{ marginTop: 8 }}>⚙️ 生成设置</h3>
<div className="xx-form-field" style={{ marginTop: 12 }}>
<label>生成数量</label>
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
<div
style={{
display: "inline-flex",
alignItems: "center",
border: "1px solid #e5e7eb",
borderRadius: 8,
overflow: "hidden",
background: "#fff",
}}
>
<button
type="button"
onClick={() => onPreviewCountChange(Math.max(1, previewCount - 1))}
disabled={previewCount <= 1}
style={{
width: 36,
height: 36,
border: "none",
background: "transparent",
cursor: previewCount <= 1 ? "not-allowed" : "pointer",
color: previewCount <= 1 ? "#d1d5db" : "#374151",
fontSize: 16,
}}
>
<MinusOutlined />
</button>
<span
style={{
minWidth: 40,
textAlign: "center",
fontSize: 16,
fontWeight: 600,
color: "#111",
}}
>
{previewCount}
</span>
<button
type="button"
onClick={() => onPreviewCountChange(Math.min(10, previewCount + 1))}
disabled={previewCount >= 10}
style={{
width: 36,
height: 36,
border: "none",
background: "transparent",
cursor: previewCount >= 10 ? "not-allowed" : "pointer",
color: previewCount >= 10 ? "#d1d5db" : "#374151",
fontSize: 16,
}}
>
<PlusOutlined />
</button>
</div>
<span style={{ fontSize: 12, color: "#6b7280" }}>最多一次生成 10 个</span>
</div>
</div>
<div className="xx-form-field" style={{ marginTop: 16 }}>
<label>视频比例</label>
<div style={{ display: "flex", gap: 12, marginTop: 4 }}>
{[
{ key: "9:16" as const, emoji: "📱", label: "竖屏 9:16" },
{ key: "16:9" as const, emoji: "🖥️", label: "横屏 16:9" },
].map((opt) => {
const selected = videoRatio === opt.key
return (
<button
key={opt.key}
type="button"
onClick={() => onVideoRatioChange(opt.key)}
style={{
padding: "10px 20px",
borderRadius: 8,
border: selected ? PURPLE_BORDER : "1px solid #e5e7eb",
background: selected ? PURPLE_BG : "#fff",
color: selected ? PURPLE : "#374151",
cursor: "pointer",
fontSize: 14,
fontWeight: selected ? 600 : 400,
transition: "all 0.2s",
}}
>
{opt.emoji} {opt.label}
</button>
)
})}
</div>
</div>
<div
className="xx-form-field"
style={{
marginTop: 16,
padding: "12px 16px",
background: "#f9fafb",
borderRadius: 8,
}}
>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<span style={{ fontSize: 14, fontWeight: 500, color: "#111" }}>
🎯 智能降重 {dedupEnabled ? "已开启" : "已关闭"}
</span>
<button
type="button"
onClick={() => onDedupEnabledChange(!dedupEnabled)}
style={{
width: 44,
height: 24,
borderRadius: 12,
border: "none",
background: dedupEnabled ? PURPLE : "#d1d5db",
position: "relative",
cursor: "pointer",
transition: "background 0.2s",
padding: 0,
flexShrink: 0,
}}
aria-label="toggle dedup"
>
<span
style={{
position: "absolute",
top: 2,
left: dedupEnabled ? 22 : 2,
width: 20,
height: 20,
borderRadius: "50%",
background: "#fff",
transition: "left 0.2s",
boxShadow: "0 1px 3px rgba(0,0,0,0.2)",
}}
/>
</button>
</div>
<div style={{ fontSize: 12, color: "#6b7280", marginTop: 4 }}>
自动对画面做微调,避免查重不过
</div>
</div>
</div>
)
}
export default Step1EditMode