-
+
+ {/* ── 页头 ── */}
+
+
+
+ 一键生成
+
+
+ 快速生成短视频,支持多种风格和素材组合
+
+
-
({ title: s.title, icon: s.icon }))}
- style={{ marginBottom: 32 }}
- responsive
- />
+
+ {/* ════════════ 左侧:主内容 ════════════ */}
+
+ {/* ── 1. 基本信息 ── */}
+
- {/* 步骤导航 */}
- {!generated && (
-
-
-
+ {/* ── 2. 素材选择 ── */}
+
+
+
+
+ 素材选择
+
+
+ 已选 {selectedMaterials.length} 个
+
+
+
+
+ {MOCK_MATERIALS.map((m) => {
+ const selected = selectedMaterials.includes(m.id);
+ return (
+
toggleMaterial(m.id)}
+ role="button"
+ tabIndex={0}
+ aria-pressed={selected}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ toggleMaterial(m.id);
+ }
+ }}
+ >
+
+ {materialIcon(m.mime_type)}
+
+
+
+ {m.name}
+
+
+ {m.mime_type.split("/")[1].toUpperCase()}
+ {m.file_size ? ` · ${formatSize(m.file_size)}` : ""}
+
+
+
+
+
+
+ );
+ })}
+
+
+ {/* 上传入口 */}
+
setFileList(fl)}
+ beforeUpload={() => false}
+ style={{ marginTop: 16 }}
+ >
+
+
+
+ 点击或拖拽素材到此区域上传
+
+ 支持视频、图片、音频格式
+
+
+
+
+
+ {/* ── 3. 配音选择 ── */}
+
+
+
+
setVoiceMode(e.target.value)}
+ style={{ marginBottom: 16 }}
+ >
+ 系统预设
+ 自定义录制
+
+
+ {voiceMode === "preset" ? (
+
+ {MOCK_VOICES.map((v) => {
+ const selected = selectedVoice === v.id;
+ return (
+
setSelectedVoice(v.id)}
+ role="button"
+ tabIndex={0}
+ aria-pressed={selected}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ setSelectedVoice(v.id);
+ }
+ }}
+ >
+
+
+
+
+ {v.name}
+
+
+ {v.voice_type}
+
+
+
+
+ ) : (
+
+ )
+ }
+ onClick={(e) => {
+ e.stopPropagation();
+ toggleVoicePlay(v.id);
+ }}
+ >
+ {playingVoice === v.id ? "停止" : "试听"}
+
+
+ );
+ })}
+
+ ) : (
+
+
+ )}
+
+
+
+ {/* ── 4. 标题/文案设置 ── */}
+
+
+
+
setTitleMode(e.target.value)}
+ style={{ marginBottom: 16 }}
+ >
+ AI 智能生成
+ 手动输入
+
+
+ {titleMode === "ai" ? (
+
+
+ 根据视频标题和描述,AI 将自动生成视频文案和字幕
+
+ }>
+ 预览 AI 生成结果
+
+
+ ) : (
+
+
+
+ {/* ── 5. 高级设置 ── */}
+
+
+
+
+ 高级设置
+
+
+ (可选,适合高级用户)
+
+
+ ),
+ children: (
+
+ ),
+ },
+ ]}
+ />
+
+
+ {/* ── 6. 生成按钮 ── */}
+ {!generated && (
+
+ {generating ? (
+
+ ) : (
+
}
+ onClick={handleGenerate}
+ >
+ 开始生成视频
+
+ )}
+
+ )}
- )}
+
+ {/* ════════════ 右侧:结果面板 ════════════ */}
+
+
+
+
+ 生成结果
+
+ {generated && 已完成}
+
+
+ {generated ? (
+ <>
+
+
+
+
+ 标题
+
+ {title || "未设置"}
+
+
+ 比例
+
+ {videoRatio}
+
+
+ 时长
+
+ {duration}s
+
+
+ 素材
+
+ {selectedMaterials.length} 个
+
+
+
+
+ }
+ block
+ >
+ 下载视频
+
+ }
+ block
+ >
+ 分享
+
+ }
+ block
+ onClick={() => {
+ setGenerated(false);
+ setProgress(0);
+ }}
+ >
+ 重新生成
+
+
+ >
+ ) : (
+
+
+
+
+ {generating ? "视频生成中…" : "配置参数后点击生成"}
+
+
+ )}
+
+
+
+
);
};
diff --git a/apps/web/src/pages/generate/generate.css b/apps/web/src/pages/generate/generate.css
new file mode 100644
index 000000000..17d0740eb
--- /dev/null
+++ b/apps/web/src/pages/generate/generate.css
@@ -0,0 +1,436 @@
+/**
+ * 一键生成页面 - V21 设计系统样式
+ * 统一使用 CSS 变量,支持深色/浅色主题
+ */
+@import "../../styles/global.css";
+
+/* ============================================================
+ 页面容器
+ ============================================================ */
+.xx-generate-page {
+ min-height: 100%;
+ padding: var(--space-xl);
+ max-width: 1400px;
+ margin: 0 auto;
+}
+
+.xx-generate-header {
+ margin-bottom: var(--space-xl);
+}
+
+.xx-generate-title {
+ font-size: var(--font-size-3xl) !important;
+ font-weight: var(--font-weight-bold) !important;
+ color: var(--text-primary) !important;
+ margin-bottom: var(--space-xs) !important;
+}
+
+.xx-generate-subtitle {
+ color: var(--text-secondary) !important;
+ font-size: var(--font-size-base) !important;
+}
+
+/* ============================================================
+ 主内容布局(左主右侧)
+ ============================================================ */
+.xx-generate-layout {
+ display: grid;
+ grid-template-columns: 1fr 380px;
+ gap: var(--space-xl);
+ align-items: start;
+}
+
+/* ============================================================
+ 区块卡片
+ ============================================================ */
+.xx-generate-section {
+ background: var(--bg-primary) !important;
+ border: 1px solid var(--border-color) !important;
+ border-radius: var(--radius-xl) !important;
+ box-shadow: var(--shadow-card) !important;
+ margin-bottom: var(--space-lg);
+ overflow: hidden;
+ transition: var(--transition-all);
+}
+
+.xx-generate-section:hover {
+ box-shadow: var(--shadow-md) !important;
+}
+
+.xx-section-header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-sm);
+ padding: var(--space-lg) var(--space-xl);
+ border-bottom: 1px solid var(--border-light);
+}
+
+.xx-section-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 36px;
+ height: 36px;
+ border-radius: var(--radius-sm);
+ background: var(--primary-soft);
+ color: var(--primary-color);
+ font-size: var(--font-size-lg);
+ flex-shrink: 0;
+}
+
+.xx-section-title {
+ font-size: var(--font-size-md) !important;
+ font-weight: var(--font-weight-semibold) !important;
+ color: var(--text-primary) !important;
+ margin: 0 !important;
+ flex: 1;
+}
+
+.xx-section-desc {
+ color: var(--text-tertiary) !important;
+ font-size: var(--font-size-sm) !important;
+ margin: 0 !important;
+}
+
+.xx-section-body {
+ padding: var(--space-xl);
+}
+
+/* ============================================================
+ 字数统计
+ ============================================================ */
+.xx-word-count {
+ text-align: right;
+ font-size: var(--font-size-sm);
+ color: var(--text-tertiary);
+ margin-top: var(--space-xs);
+ transition: var(--transition-colors);
+}
+
+.xx-word-count-warn {
+ color: var(--warning-color);
+}
+
+.xx-word-count-over {
+ color: var(--error-color);
+}
+
+/* ============================================================
+ 素材选择
+ ============================================================ */
+.xx-material-grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: var(--space-md);
+}
+
+.xx-material-card {
+ position: relative;
+ border-radius: var(--radius-md);
+ border: 2px solid var(--border-color);
+ overflow: hidden;
+ cursor: pointer;
+ transition: var(--transition-all);
+ background: var(--bg-primary);
+}
+
+.xx-material-card:hover {
+ border-color: var(--primary-color);
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-sm);
+}
+
+.xx-material-card-selected {
+ border-color: var(--primary-color) !important;
+ box-shadow: var(--shadow-primary) !important;
+}
+
+.xx-material-thumb {
+ height: 100px;
+ background: var(--bg-tertiary);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-tertiary);
+ font-size: var(--font-size-3xl);
+ overflow: hidden;
+}
+
+.xx-material-info {
+ padding: var(--space-sm) var(--space-md);
+}
+
+.xx-material-name {
+ font-size: var(--font-size-sm) !important;
+ font-weight: var(--font-weight-medium) !important;
+ color: var(--text-primary) !important;
+ margin: 0 !important;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.xx-material-meta {
+ font-size: var(--font-size-xs) !important;
+ color: var(--text-tertiary) !important;
+ margin: 0 !important;
+}
+
+.xx-material-check {
+ position: absolute;
+ top: var(--space-sm);
+ right: var(--space-sm);
+ width: 24px;
+ height: 24px;
+ border-radius: var(--radius-full);
+ background: var(--gradient-primary);
+ color: var(--text-inverse);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: var(--font-size-sm);
+ opacity: 0;
+ transform: scale(0.6);
+ transition: var(--transition-all);
+}
+
+.xx-material-card-selected .xx-material-check {
+ opacity: 1;
+ transform: scale(1);
+}
+
+/* ============================================================
+ 配音卡片
+ ============================================================ */
+.xx-voice-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: var(--space-md);
+}
+
+.xx-voice-card {
+ border-radius: var(--radius-md);
+ border: 2px solid var(--border-color);
+ padding: var(--space-md);
+ cursor: pointer;
+ transition: var(--transition-all);
+ background: var(--bg-primary);
+}
+
+.xx-voice-card:hover {
+ border-color: var(--primary-color);
+}
+
+.xx-voice-card-selected {
+ border-color: var(--primary-color) !important;
+ background: var(--primary-soft) !important;
+}
+
+.xx-voice-header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-sm);
+ margin-bottom: var(--space-sm);
+}
+
+.xx-voice-avatar {
+ width: 36px;
+ height: 36px;
+ border-radius: var(--radius-full);
+ background: var(--gradient-primary);
+ color: var(--text-inverse);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: var(--font-size-md);
+ flex-shrink: 0;
+}
+
+.xx-voice-name {
+ font-size: var(--font-size-base) !important;
+ font-weight: var(--font-weight-semibold) !important;
+ color: var(--text-primary) !important;
+ margin: 0 !important;
+}
+
+.xx-voice-desc {
+ font-size: var(--font-size-xs) !important;
+ color: var(--text-tertiary) !important;
+ margin: 0 !important;
+}
+
+/* ============================================================
+ 单选卡片组(比例 / 风格)
+ ============================================================ */
+.xx-radio-card-group {
+ display: flex;
+ gap: var(--space-md);
+ flex-wrap: wrap;
+}
+
+.xx-radio-card {
+ flex: 1;
+ min-width: 100px;
+ border-radius: var(--radius-md);
+ border: 2px solid var(--border-color);
+ padding: var(--space-md);
+ cursor: pointer;
+ text-align: center;
+ transition: var(--transition-all);
+ background: var(--bg-primary);
+}
+
+.xx-radio-card:hover {
+ border-color: var(--primary-color);
+}
+
+.xx-radio-card-selected {
+ border-color: var(--primary-color) !important;
+ background: var(--primary-soft) !important;
+}
+
+.xx-radio-card-label {
+ font-size: var(--font-size-base) !important;
+ font-weight: var(--font-weight-semibold) !important;
+ color: var(--text-primary) !important;
+ margin: 0 !important;
+}
+
+.xx-radio-card-desc {
+ font-size: var(--font-size-xs) !important;
+ color: var(--text-tertiary) !important;
+ margin: 0 !important;
+ margin-top: 2px !important;
+}
+
+/* ============================================================
+ 风格卡片
+ ============================================================ */
+.xx-style-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: var(--space-md);
+}
+
+.xx-style-card {
+ border-radius: var(--radius-md);
+ border: 2px solid var(--border-color);
+ padding: var(--space-md);
+ cursor: pointer;
+ transition: var(--transition-all);
+ background: var(--bg-primary);
+ text-align: center;
+}
+
+.xx-style-card:hover {
+ border-color: var(--primary-color);
+}
+
+.xx-style-card-selected {
+ border-color: var(--primary-color) !important;
+ background: var(--primary-soft) !important;
+}
+
+.xx-style-icon {
+ font-size: var(--font-size-2xl);
+ color: var(--primary-color);
+ margin-bottom: var(--space-xs);
+}
+
+.xx-style-name {
+ font-size: var(--font-size-sm) !important;
+ font-weight: var(--font-weight-semibold) !important;
+ color: var(--text-primary) !important;
+ margin: 0 !important;
+}
+
+.xx-style-desc {
+ font-size: var(--font-size-xs) !important;
+ color: var(--text-tertiary) !important;
+ margin: 0 !important;
+}
+
+/* ============================================================
+ 生成区域
+ ============================================================ */
+.xx-generate-action {
+ text-align: center;
+ padding: var(--space-xl) 0;
+}
+
+/* ============================================================
+ 结果区域
+ ============================================================ */
+.xx-result-preview {
+ border-radius: var(--radius-lg);
+ background: var(--bg-tertiary);
+ height: 200px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-tertiary);
+ font-size: var(--font-size-4xl);
+ margin-bottom: var(--space-md);
+ overflow: hidden;
+}
+
+.xx-result-actions {
+ display: flex;
+ gap: var(--space-sm);
+}
+
+/* ============================================================
+ 响应式
+ ============================================================ */
+@media (max-width: 1200px) {
+ .xx-generate-layout {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 768px) {
+ .xx-generate-page {
+ padding: var(--space-md);
+ }
+
+ .xx-material-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .xx-voice-grid,
+ .xx-style-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .xx-section-header {
+ padding: var(--space-md);
+ }
+
+ .xx-section-body {
+ padding: var(--space-md);
+ }
+
+ .xx-generate-title {
+ font-size: var(--font-size-2xl) !important;
+ }
+}
+
+@media (max-width: 480px) {
+ .xx-generate-page {
+ padding: var(--space-sm);
+ }
+
+ .xx-material-grid,
+ .xx-voice-grid,
+ .xx-style-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .xx-radio-card-group {
+ flex-direction: column;
+ }
+
+ .xx-result-actions {
+ flex-direction: column;
+ }
+}