Files
AIGC/demo/frontend/src/views/StoryboardVideoCreate.vue
2025-10-21 16:50:33 +08:00

733 lines
14 KiB
Vue
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.

<template>
<div class="storyboard-video-create-page">
<!-- 顶部导航栏 -->
<header class="top-header">
<div class="header-left">
<button class="back-btn" @click="goBack">
首页
</button>
</div>
<div class="header-right">
<div class="credits-info">
<div class="credits-circle">25</div>
<span>| 首购优惠</span>
</div>
<div class="notification-icon">
🔔
<div class="notification-badge">5</div>
</div>
<div class="user-avatar">
👤
</div>
</div>
</header>
<!-- 主内容区域 -->
<div class="main-content">
<!-- 左侧设置面板 -->
<div class="left-panel">
<!-- 创作模式标签 -->
<div class="creation-tabs">
<div class="tab" @click="goToTextToVideo">文生视频</div>
<div class="tab" @click="goToImageToVideo">图生视频</div>
<div class="tab active">分镜视频</div>
</div>
<!-- 分镜步骤标签 -->
<div class="storyboard-steps">
<div class="step active">生成分镜图</div>
<div class="step">生成视频</div>
</div>
<!-- 生成分镜图区域 -->
<div class="storyboard-section">
<div class="image-upload-btn" @click="uploadImage">
<span>+ 图片 (可选)</span>
</div>
<!-- 已上传的图片预览 -->
<div class="image-preview" v-if="uploadedImage">
<img :src="uploadedImage" alt="上传的图片" />
<button class="remove-btn" @click="removeImage">×</button>
</div>
<div class="text-input-section">
<textarea
v-model="inputText"
placeholder="例如:一个咖啡的广告提示:简单描述即可,AI会自动优化成专业的12格黑白分镜图"
class="text-input"
rows="6"
></textarea>
<div class="optimize-btn">
<button class="optimize-button">
一键优化
</button>
</div>
</div>
</div>
<!-- 视频设置 -->
<div class="video-settings">
<div class="setting-item">
<label>比例</label>
<select v-model="aspectRatio" class="setting-select">
<option value="16:9">16:9</option>
<option value="4:3">4:3</option>
<option value="1:1">1:1</option>
<option value="3:4">3:4</option>
<option value="9:16">9:16</option>
</select>
</div>
<div class="setting-item">
<label>高清模式 (1080P)</label>
<div class="hd-setting">
<input type="checkbox" v-model="hdMode" class="hd-switch">
<span class="cost-text">开启消耗20积分</span>
</div>
</div>
</div>
<!-- 生成按钮 -->
<div class="generate-section">
<button class="generate-btn" @click="startGenerate">
开始生成
</button>
</div>
</div>
<!-- 右侧预览区域 -->
<div class="right-panel">
<div class="preview-area">
<div class="status-checkbox">
<input type="checkbox" v-model="inProgress" id="progress-checkbox">
<label for="progress-checkbox">进行中</label>
</div>
<div class="preview-content">
<div class="preview-placeholder">
<div class="placeholder-text">开始创作您的第一个作品吧!</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
// 表单数据
const inputText = ref('')
const aspectRatio = ref('16:9')
const hdMode = ref(false)
const inProgress = ref(false)
// 图片上传
const uploadedImage = ref('')
// 导航函数
const goBack = () => {
router.back()
}
const goToTextToVideo = () => {
router.push('/text-to-video/create')
}
const goToImageToVideo = () => {
router.push('/image-to-video/create')
}
// 图片上传处理
const uploadImage = () => {
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.onchange = (e) => {
const file = e.target.files[0]
if (file) {
const reader = new FileReader()
reader.onload = (e) => {
uploadedImage.value = e.target.result
}
reader.readAsDataURL(file)
}
}
input.click()
}
const removeImage = () => {
uploadedImage.value = ''
}
const startGenerate = () => {
if (!inputText.value.trim()) {
alert('请输入描述文字')
return
}
inProgress.value = true
alert('开始生成分镜图...')
// 模拟生成过程
setTimeout(() => {
inProgress.value = false
alert('分镜图生成完成!')
}, 3000)
}
</script>
<style scoped>
.storyboard-video-create-page {
height: 100vh;
background: #0a0a0a;
color: #fff;
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* 顶部导航栏 */
.top-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 32px;
background: #0a0a0a;
border-bottom: 1px solid #1f1f1f;
min-height: 60px;
}
.header-left {
display: flex;
align-items: center;
}
.back-btn {
background: none;
border: none;
color: #fff;
font-size: 16px;
cursor: pointer;
padding: 10px 20px;
border-radius: 8px;
transition: all 0.2s ease;
font-weight: 500;
}
.back-btn:hover {
background: #1a1a1a;
transform: translateX(-2px);
}
.header-right {
display: flex;
align-items: center;
gap: 24px;
}
.credits-info {
display: flex;
align-items: center;
gap: 12px;
color: #fff;
font-size: 14px;
font-weight: 500;
}
.credits-circle {
width: 36px;
height: 36px;
border-radius: 50%;
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 16px;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
.notification-icon {
position: relative;
font-size: 24px;
cursor: pointer;
padding: 8px;
border-radius: 8px;
transition: background 0.2s ease;
}
.notification-icon:hover {
background: #1a1a1a;
}
.notification-badge {
position: absolute;
top: 2px;
right: 2px;
background: #ef4444;
color: #fff;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 700;
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.4);
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, #374151, #1f2937);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 20px;
transition: transform 0.2s ease;
}
.user-avatar:hover {
transform: scale(1.05);
}
/* 主内容区域 */
.main-content {
flex: 1;
display: grid;
grid-template-columns: 400px 1fr;
gap: 0;
height: calc(100vh - 100px);
}
/* 左侧面板 */
.left-panel {
background: #1a1a1a;
border-right: 1px solid #2a2a2a;
padding: 32px;
display: flex;
flex-direction: column;
gap: 32px;
overflow-y: auto;
}
/* 创作模式标签 */
.creation-tabs {
display: flex;
gap: 4px;
background: #0a0a0a;
padding: 4px;
border-radius: 12px;
}
.tab {
flex: 1;
padding: 12px 16px;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
color: #9ca3af;
font-size: 14px;
font-weight: 500;
text-align: center;
}
.tab.active {
background: #3b82f6;
color: #fff;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}
.tab:hover:not(.active) {
background: #2a2a2a;
color: #fff;
}
/* 分镜步骤标签 */
.storyboard-steps {
display: flex;
gap: 4px;
background: #0a0a0a;
padding: 4px;
border-radius: 12px;
}
.step {
flex: 1;
padding: 10px 16px;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
color: #9ca3af;
font-size: 13px;
font-weight: 500;
text-align: center;
}
.step.active {
background: #3b82f6;
color: #fff;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}
.step:hover:not(.active) {
background: #2a2a2a;
color: #fff;
}
/* 分镜图区域 */
.storyboard-section {
display: flex;
flex-direction: column;
gap: 16px;
}
.image-upload-btn {
width: 100%;
padding: 12px 16px;
background: #0a0a0a;
border: 2px dashed #2a2a2a;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
color: #9ca3af;
font-size: 14px;
}
.image-upload-btn:hover {
border-color: #3b82f6;
background: #1a1a1a;
color: #fff;
}
.image-preview {
position: relative;
width: 100%;
aspect-ratio: 16/9;
}
.image-preview img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 8px;
}
.remove-btn {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.7);
color: #fff;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: bold;
}
/* 文本输入区域 */
.text-input-section {
display: flex;
flex-direction: column;
gap: 16px;
}
.text-input {
width: 100%;
min-height: 120px;
padding: 16px;
background: #0a0a0a;
border: 2px solid #2a2a2a;
border-radius: 12px;
color: #fff;
font-size: 15px;
line-height: 1.6;
resize: vertical;
outline: none;
transition: all 0.2s ease;
font-family: inherit;
}
.text-input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.text-input::placeholder {
color: #6b7280;
}
.optimize-btn {
display: flex;
justify-content: flex-end;
}
.optimize-button {
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}
.optimize-button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
/* 视频设置 */
.video-settings {
display: flex;
flex-direction: column;
gap: 20px;
}
.setting-item {
display: flex;
flex-direction: column;
gap: 10px;
}
.setting-item label {
font-size: 14px;
color: #e5e7eb;
font-weight: 600;
}
.setting-select {
padding: 12px 16px;
background: #0a0a0a;
border: 2px solid #2a2a2a;
border-radius: 8px;
color: #fff;
font-size: 14px;
outline: none;
cursor: pointer;
transition: all 0.2s ease;
font-family: inherit;
}
.setting-select:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.setting-select:hover {
border-color: #374151;
}
.hd-setting {
display: flex;
align-items: center;
gap: 12px;
}
.hd-switch {
width: 20px;
height: 20px;
cursor: pointer;
accent-color: #3b82f6;
}
.cost-text {
font-size: 13px;
color: #9ca3af;
font-weight: 500;
}
/* 生成按钮 */
.generate-section {
margin-top: auto;
}
.generate-btn {
width: 100%;
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
color: #fff;
border: none;
padding: 16px 24px;
border-radius: 12px;
font-size: 16px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.3);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.generate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.4);
}
.generate-btn:active {
transform: translateY(0);
}
.generate-btn:disabled {
background: #6b7280;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* 右侧面板 */
.right-panel {
background: #0a0a0a;
padding: 32px;
display: flex;
flex-direction: column;
}
.preview-area {
flex: 1;
display: flex;
flex-direction: column;
gap: 20px;
}
.status-checkbox {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}
.status-checkbox input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #3b82f6;
}
.status-checkbox label {
font-size: 14px;
color: #e5e7eb;
cursor: pointer;
font-weight: 500;
}
.preview-content {
flex: 1;
background: #1a1a1a;
border: 2px solid #2a2a2a;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.preview-content:hover {
border-color: #374151;
}
.preview-placeholder {
text-align: center;
padding: 40px;
}
.placeholder-text {
font-size: 18px;
color: #6b7280;
font-weight: 500;
line-height: 1.5;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.main-content {
grid-template-columns: 350px 1fr;
}
.left-panel {
padding: 24px;
}
.right-panel {
padding: 24px;
}
}
@media (max-width: 1024px) {
.main-content {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
.left-panel {
border-right: none;
border-bottom: 1px solid #2a2a2a;
padding: 20px;
}
.right-panel {
padding: 20px;
}
}
@media (max-width: 768px) {
.top-header {
padding: 16px 20px;
}
.header-right {
gap: 16px;
}
.left-panel {
padding: 16px;
gap: 24px;
}
.right-panel {
padding: 16px;
}
.creation-tabs {
flex-direction: column;
gap: 8px;
}
.tab {
text-align: left;
}
.storyboard-steps {
flex-direction: column;
gap: 8px;
}
.step {
text-align: left;
}
}
</style>