Files
xiaoxia-saas/apps/web/src/pages/generate/generate.css
T
灵应 c9c7c36b1d
Deploy / Deploy Staging (push) Failing after 96h30m21s
Deploy / Staging E2E Tests (push) Has been skipped
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 96h30m30s
CI/CD Pipeline / Frontend Lint (push) Failing after 96h30m30s
feat(generate): V21 原型 1:1 还原一键生成页面 — 5步向导 + 左右布局
- 5 步向导流程:选择模板→选择素材→选择标题→选择配音→确认生成
- steps-bar + step-item 步骤条组件(active/done 状态)
- 左右布局:generate-form(表单区)+ generate-preview(预览区)
- 步骤1:choice-list 卡片式模板选择(画中画混剪/一镜到底/人物口播)
- 步骤2:素材库下拉 + 已选素材 pill 显示
- 步骤3:标题下拉选择 + 手动输入
- 步骤4:voice-choice-list 配音卡片(温柔女声/专业男声/活力女声/克隆我的声音)
- 步骤5:确认摘要卡片
- 预览区:视频预览 + 字幕 + 时间线 + 重新生成/确认生成
- 保留所有现有业务逻辑(API 调用、URL 参数、CloneModal、TTS)
- generate.css 完全匹配原型 CSS 变量、间距、圆角、阴影
- 响应式:1200px/768px/480px 断点适配
- tsc + vite build 零错误

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 12:23:54 +08:00

837 lines
16 KiB
CSS
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.
/**
* 一键生成页面 — V21 原型 1:1 还原样式
* 对照 frontend-v21-ui-prototype-final.html #view-generate
*/
@import "../../styles/global.css";
/* ============================================================
页面容器
============================================================ */
.xx-generate-page {
min-height: 100%;
padding: var(--space-xl);
max-width: 1400px;
margin: 0 auto;
}
/* ============================================================
页头
============================================================ */
.xx-generate-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: var(--space-xl);
}
.xx-generate-head h2 {
margin: 0 0 4px;
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
color: var(--text-primary);
}
.xx-generate-head p {
margin: 0;
color: var(--text-secondary);
font-size: var(--font-size-base);
}
/* ============================================================
步骤条 steps-bar
============================================================ */
.xx-steps-bar {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
margin-bottom: 28px;
}
.xx-step-item {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
border-radius: var(--radius-sm, 10px);
color: var(--text-tertiary, #94a3b8);
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
user-select: none;
}
.xx-step-item:hover {
background: var(--bg-secondary, #f8fafc);
}
.xx-step-item.active {
background: var(--primary-soft, #eef2ff);
color: var(--primary-color, #4f46e5);
}
.xx-step-item.done {
color: var(--secondary-color, #10b981);
}
.xx-step-num {
width: 26px;
height: 26px;
border-radius: 50%;
background: #f1f5f9;
display: grid;
place-items: center;
font-size: 12px;
font-weight: 700;
flex-shrink: 0;
}
.xx-step-item.active .xx-step-num {
background: var(--primary-color, #4f46e5);
color: #fff;
}
.xx-step-item.done .xx-step-num {
background: var(--secondary-color, #10b981);
color: #fff;
}
.xx-step-arrow {
color: #cbd5e1;
font-size: 14px;
user-select: none;
}
/* ============================================================
主布局 generate-layout
============================================================ */
.xx-generate-layout {
display: grid;
grid-template-columns: 1fr 320px;
gap: 24px;
align-items: start;
}
/* ============================================================
左侧表单区 generate-form
============================================================ */
.xx-generate-form {
display: flex;
flex-direction: column;
gap: 16px;
}
/* ── form-section 卡片 ── */
.xx-form-section {
background: #fff;
border: 1px solid var(--border-color, #e2e8f0);
border-radius: var(--radius-md, 14px);
padding: 20px;
}
.xx-form-section h3 {
margin: 0 0 14px;
font-size: 15px;
font-weight: 600;
color: var(--text-primary);
}
/* ── form-field ── */
.xx-form-field {
margin-bottom: 14px;
}
.xx-form-field:last-child {
margin-bottom: 0;
}
.xx-form-field label {
display: block;
font-weight: 600;
margin-bottom: 8px;
font-size: 13px;
color: var(--text-primary);
}
.xx-form-field select,
.xx-form-field input {
width: 100%;
height: 44px;
border: 1px solid var(--border-color, #e2e8f0);
border-radius: var(--radius-sm, 10px);
background: #fff;
padding: 0 14px;
font-size: 14px;
outline: 0;
transition: 0.15s ease;
color: var(--text-primary);
}
.xx-form-field select:focus,
.xx-form-field input:focus {
border-color: var(--primary-color, #4f46e5);
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
/* ── form-row(双列) ── */
.xx-form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
/* ============================================================
choice-list 卡片式选择
============================================================ */
.xx-choice-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.xx-choice-item {
position: relative;
background: #fff;
border: 2px solid var(--border-color, #e2e8f0);
border-radius: var(--radius-md, 14px);
padding: 14px;
cursor: pointer;
transition: 0.18s ease;
text-align: center;
}
.xx-choice-item:hover {
border-color: #c7d2fe;
}
.xx-choice-item.selected {
border-color: var(--primary-color, #4f46e5);
background: var(--primary-soft, #eef2ff);
}
.xx-choice-thumb {
height: 60px;
border-radius: var(--radius-sm, 10px);
display: grid;
place-items: center;
color: #fff;
font-size: 24px;
font-weight: 700;
margin-bottom: 10px;
}
.xx-choice-item h4 {
margin: 0 0 4px;
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
}
.xx-choice-item p {
margin: 0;
font-size: 11px;
color: var(--text-tertiary, #94a3b8);
}
.xx-choice-check {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--primary-color, #4f46e5);
color: #fff;
display: none;
place-items: center;
font-size: 12px;
position: absolute;
right: -6px;
top: -6px;
}
.xx-choice-item.selected .xx-choice-check {
display: grid;
}
/* ============================================================
Pill 标签
============================================================ */
.xx-pill {
font-size: 11px;
padding: 4px 8px;
border-radius: 999px;
font-weight: 700;
display: inline-block;
}
.xx-pill-ok {
background: #dcfce7;
color: #166534;
}
.xx-pill-info {
background: #dbeafe;
color: #1d4ed8;
}
/* ============================================================
配音卡片(步骤4 choice-list 变体)
============================================================ */
.xx-voice-choice-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.xx-voice-choice-item {
position: relative;
background: #fff;
border: 2px solid var(--border-color, #e2e8f0);
border-radius: var(--radius-md, 14px);
padding: 16px;
cursor: pointer;
transition: 0.18s ease;
text-align: left;
display: flex;
align-items: center;
gap: 12px;
}
.xx-voice-choice-item:hover {
border-color: #c7d2fe;
}
.xx-voice-choice-item.selected {
border-color: var(--primary-color, #4f46e5);
background: var(--primary-soft, #eef2ff);
}
.xx-voice-choice-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, #6366f1, #4f46e5);
color: #fff;
display: grid;
place-items: center;
font-size: 18px;
flex-shrink: 0;
}
.xx-voice-choice-info h4 {
margin: 0 0 2px;
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
}
.xx-voice-choice-info p {
margin: 0;
font-size: 11px;
color: var(--text-tertiary, #94a3b8);
}
.xx-voice-choice-check {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--primary-color, #4f46e5);
color: #fff;
display: none;
place-items: center;
font-size: 12px;
position: absolute;
right: -6px;
top: -6px;
}
.xx-voice-choice-item.selected .xx-voice-choice-check {
display: grid;
}
/* ── 克隆声音区域 ── */
.xx-clone-section {
margin-top: 16px;
padding: 16px;
background: var(--bg-secondary, #f8fafc);
border: 1px solid var(--border-color, #e2e8f0);
border-radius: var(--radius-md, 14px);
}
.xx-clone-section-title {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
margin: 0 0 8px;
}
.xx-clone-voices-list {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.xx-clone-voice-row {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: #fff;
border: 1px solid var(--border-color, #e2e8f0);
border-radius: var(--radius-sm, 10px);
cursor: pointer;
transition: 0.15s ease;
}
.xx-clone-voice-row:hover {
border-color: var(--primary-color, #4f46e5);
}
.xx-clone-voice-row.selected {
border-color: var(--primary-color, #4f46e5);
background: var(--primary-soft, #eef2ff);
}
.xx-clone-voice-row.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.xx-clone-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 14px;
flex-shrink: 0;
}
.xx-clone-avatar.ready {
background: linear-gradient(135deg, #10b981, #059669);
color: #fff;
}
.xx-clone-avatar.processing {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: #fff;
animation: xx-clone-pulse 2s ease-in-out infinite;
}
.xx-clone-avatar.failed {
background: linear-gradient(135deg, #94a3b8, #64748b);
color: #fff;
}
@keyframes xx-clone-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
.xx-clone-info {
flex: 1;
min-width: 0;
}
.xx-clone-name {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
}
.xx-clone-status {
font-size: 11px;
display: flex;
align-items: center;
gap: 4px;
}
.xx-clone-status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
}
/* ── 克隆进度条 ── */
.xx-clone-progress {
position: relative;
height: 4px;
background: #f1f5f9;
border-radius: 3px;
margin-top: 6px;
overflow: hidden;
}
.xx-clone-progress-bar {
height: 100%;
background: linear-gradient(90deg, #f59e0b, #10b981);
border-radius: 3px;
transition: width 0.5s ease;
}
.xx-clone-progress--indeterminate .xx-clone-progress-bar {
width: 100%;
background: repeating-linear-gradient(
90deg,
#f59e0b 0%, #f59e0b 25%,
#10b981 25%, #10b981 50%,
#f59e0b 50%
);
background-size: 60px 100%;
animation: xx-clone-progress-flow 1.2s linear infinite;
}
@keyframes xx-clone-progress-flow {
from { background-position: 0 0; }
to { background-position: 60px 0; }
}
.xx-clone-progress-text {
position: absolute;
right: 0;
top: -14px;
font-size: 10px;
color: #f59e0b;
font-weight: 600;
}
/* ── 轮询提示 ── */
.xx-clone-polling-hint {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
font-size: 12px;
color: #f59e0b;
background: rgba(245, 158, 11, 0.08);
border-radius: var(--radius-sm, 10px);
animation: xx-fade 2s ease-in-out infinite;
}
.xx-clone-polling-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: #f59e0b;
animation: xx-blink 1.5s ease-in-out infinite;
}
@keyframes xx-fade {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes xx-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
/* ============================================================
确认生成(步骤5)摘要
============================================================ */
.xx-summary-card {
background: #f8fafc;
border-radius: 14px;
padding: 16px;
}
.xx-summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.xx-summary-row + .xx-summary-row {
border-top: 1px solid var(--border-color, #e2e8f0);
}
.xx-summary-label {
color: var(--text-tertiary, #94a3b8);
font-size: 13px;
}
.xx-summary-value {
font-weight: 600;
font-size: 13px;
color: var(--text-primary);
}
/* ============================================================
底部操作按钮
============================================================ */
.xx-step-actions {
display: flex;
gap: 12px;
margin-top: 8px;
}
.xx-step-actions .xx-btn-ghost {
flex: 0 0 auto;
}
.xx-step-actions .xx-btn-primary {
flex: 1;
}
/* ============================================================
按钮(匹配原型 .btn .ghost / .btn .primary
============================================================ */
.xx-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
height: 42px;
padding: 0 20px;
border-radius: var(--radius-sm, 10px);
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.15s ease;
border: none;
outline: none;
white-space: nowrap;
}
.xx-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.xx-btn-primary {
background: linear-gradient(135deg, #6366f1, #4f46e5);
color: #fff;
box-shadow: 0 14px 26px rgba(79, 70, 229, 0.22);
}
.xx-btn-primary:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 18px 34px rgba(79, 70, 229, 0.28);
}
.xx-btn-ghost {
background: #fff;
border: 1px solid var(--border-color, #e2e8f0);
color: #475569;
}
.xx-btn-ghost:hover:not(:disabled) {
border-color: #c7d2fe;
color: #4338ca;
background: #f8faff;
}
/* ============================================================
右侧预览区 generate-preview
============================================================ */
.xx-generate-preview {
background: #fff;
border: 1px solid var(--border-color, #e2e8f0);
border-radius: var(--radius-md, 14px);
padding: 20px;
position: sticky;
top: 92px;
}
/* ── 视频预览 ── */
.xx-preview-video {
aspect-ratio: 9 / 16;
max-height: 400px;
border-radius: var(--radius-md, 14px);
background: linear-gradient(135deg, #111827, #312e81);
display: grid;
place-items: center;
color: #fff;
font-size: 36px;
position: relative;
overflow: hidden;
margin-bottom: 16px;
}
.xx-preview-video::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(circle at 72% 28%, rgba(255, 255, 255, 0.2), transparent 40%);
pointer-events: none;
}
.xx-preview-video video {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: var(--radius-md, 14px);
position: relative;
z-index: 1;
}
.xx-play-btn {
position: relative;
z-index: 1;
width: 60px;
height: 60px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: grid;
place-items: center;
backdrop-filter: blur(8px);
border: none;
color: #fff;
font-size: 24px;
cursor: pointer;
transition: all 0.2s ease;
}
.xx-play-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.05);
}
/* ── 字幕预览 ── */
.xx-preview-caption {
background: rgba(255, 255, 255, 0.95);
color: var(--text-primary, #334155);
border-radius: 10px;
padding: 10px;
text-align: center;
font-weight: 600;
font-size: 14px;
margin-bottom: 14px;
}
/* ── 时间线标题 ── */
.xx-preview-title {
font-weight: 600;
font-size: 14px;
color: var(--text-primary);
margin-bottom: 10px;
}
/* ── 时间线列表 ── */
.xx-preview-timeline {
display: flex;
flex-direction: column;
gap: 8px;
}
.xx-timeline-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
background: #f8fafc;
border-radius: 10px;
font-size: 13px;
}
.xx-timeline-item .num {
width: 24px;
height: 24px;
border-radius: 8px;
background: var(--primary-soft, #eef2ff);
color: var(--primary-color, #4f46e5);
display: grid;
place-items: center;
font-weight: 700;
font-size: 11px;
flex-shrink: 0;
}
.xx-timeline-item span {
color: var(--text-tertiary, #94a3b8);
margin-left: auto;
font-size: 12px;
}
.xx-timeline-item .scene-name {
color: var(--text-primary);
margin-left: 0;
flex: 1;
font-size: 13px;
}
/* ── 生成操作按钮 ── */
.xx-generate-actions {
display: flex;
gap: 10px;
margin-top: 16px;
}
.xx-generate-actions .xx-btn {
flex: 1;
}
/* ============================================================
生成进度
============================================================ */
.xx-progress-bar {
width: 100%;
height: 6px;
background: #f1f5f9;
border-radius: 3px;
overflow: hidden;
margin-bottom: 8px;
}
.xx-progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, #6366f1, #4f46e5);
border-radius: 3px;
transition: width 0.3s ease;
}
/* ============================================================
响应式
============================================================ */
@media (max-width: 1200px) {
.xx-generate-layout {
grid-template-columns: 1fr;
}
.xx-generate-preview {
position: static;
}
}
@media (max-width: 768px) {
.xx-generate-page {
padding: var(--space-md);
}
.xx-choice-list {
grid-template-columns: repeat(2, 1fr);
}
.xx-voice-choice-list {
grid-template-columns: 1fr;
}
.xx-form-row {
grid-template-columns: 1fr;
}
.xx-step-label {
display: none;
}
}
@media (max-width: 480px) {
.xx-generate-page {
padding: var(--space-sm);
}
.xx-choice-list {
grid-template-columns: 1fr;
}
.xx-step-actions {
flex-direction: column;
}
.xx-generate-actions {
flex-direction: column;
}
}