Initial commit: AIGC项目完整代码

This commit is contained in:
AIGC Developer
2025-10-21 16:50:33 +08:00
commit 47c8e02ab0
137 changed files with 30676 additions and 0 deletions

View File

@@ -0,0 +1,756 @@
<template>
<div class="video-detail-page">
<!-- 左侧导航栏 -->
<aside class="sidebar">
<!-- Logo -->
<div class="logo">logo</div>
<!-- 导航菜单 -->
<nav class="nav-menu">
<div class="nav-item" @click="goToProfile">
<el-icon><User /></el-icon>
<span>个人主页</span>
</div>
<div class="nav-item" @click="goToSubscription">
<el-icon><Compass /></el-icon>
<span>会员订阅</span>
</div>
<div class="nav-item active">
<el-icon><Document /></el-icon>
<span>我的作品</span>
</div>
</nav>
</aside>
<!-- 主内容区域 -->
<main class="main-content">
<!-- 左侧视频播放器区域 -->
<div class="video-player-section">
<div class="video-container">
<video
ref="videoPlayer"
class="video-player"
:src="videoData.videoUrl"
:poster="videoData.cover"
@loadedmetadata="onVideoLoaded"
@timeupdate="onTimeUpdate"
@ended="onVideoEnded"
>
您的浏览器不支持视频播放
</video>
<!-- 视频文字叠加 -->
<div class="video-overlay" v-if="videoData.overlayText">
<div class="overlay-text">{{ videoData.overlayText }}</div>
</div>
<!-- 播放控制栏 -->
<div class="video-controls">
<div class="control-left">
<button class="play-btn" @click="togglePlay">
<el-icon v-if="!isPlaying"><VideoPlay /></el-icon>
<el-icon v-else><Pause /></el-icon>
</button>
<div class="progress-container">
<div class="progress-bar" @click="seekTo">
<div class="progress-fill" :style="{ width: progressPercent + '%' }"></div>
</div>
<div class="time-display">{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</div>
</div>
</div>
<div class="control-right">
<button class="control-btn" @click="toggleFullscreen">
<el-icon><FullScreen /></el-icon>
</button>
</div>
</div>
<!-- 右上角操作按钮 -->
<div class="video-actions">
<el-tooltip content="分享" placement="bottom">
<button class="action-btn" @click="shareVideo">
<el-icon><Share /></el-icon>
</button>
</el-tooltip>
<el-tooltip content="下载" placement="bottom">
<button class="action-btn" @click="downloadVideo">
<el-icon><Download /></el-icon>
</button>
</el-tooltip>
<el-tooltip content="删除" placement="bottom">
<button class="action-btn delete-btn" @click="deleteVideo">
<el-icon><Delete /></el-icon>
</button>
</el-tooltip>
</div>
</div>
</div>
<!-- 右侧详情侧边栏 -->
<div class="detail-sidebar">
<!-- 用户信息头部 -->
<div class="sidebar-header">
<div class="user-info">
<div class="avatar">
<el-icon><User /></el-icon>
</div>
<div class="username">{{ videoData.username }}</div>
</div>
<button class="close-btn" @click="goBack">
<el-icon><Close /></el-icon>
</button>
</div>
<!-- 标签页 -->
<div class="tabs">
<div class="tab active">视频详情</div>
<div class="tab">文生视频</div>
</div>
<!-- 描述区域 -->
<div class="description-section">
<h3 class="section-title">描述</h3>
<p class="description-text">{{ videoData.description }}</p>
</div>
<!-- 元数据区域 -->
<div class="metadata-section">
<div class="metadata-item">
<span class="label">创建时间</span>
<span class="value">{{ videoData.createTime }}</span>
</div>
<div class="metadata-item">
<span class="label">视频 ID</span>
<span class="value">{{ videoData.id }}</span>
</div>
<div class="metadata-item">
<span class="label">时长</span>
<span class="value">{{ videoData.duration }}s</span>
</div>
<div class="metadata-item">
<span class="label">清晰度</span>
<span class="value">{{ videoData.resolution }}</span>
</div>
<div class="metadata-item">
<span class="label">分类</span>
<span class="value">{{ videoData.category }}</span>
</div>
<div class="metadata-item">
<span class="label">宽高比</span>
<span class="value">{{ videoData.aspectRatio }}</span>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-section">
<button class="create-similar-btn" @click="createSimilar">
做同款
</button>
</div>
</div>
</main>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
VideoPlay,
VideoPause as Pause,
FullScreen,
Share,
Download,
Delete,
User,
Compass,
Document,
Close
} from '@element-plus/icons-vue'
const route = useRoute()
const router = useRouter()
// 视频播放器相关
const videoPlayer = ref(null)
const isPlaying = ref(false)
const currentTime = ref(0)
const duration = ref(0)
const progressPercent = ref(0)
// 视频数据
const videoData = ref({
id: '2995697841305810',
username: 'mingzi_FBx7foZYDS7inL',
title: 'What Does it Mean To You',
overlayText: 'What Does it Mean To You',
description: '影片捕捉了暴风雪中的午夜时分,坐落在积雪覆盖的悬崖顶上的孤立灯塔。相机逐渐放大灯塔的灯光,穿透飞舞的雪花,投射出幽幽的光芒。在白茫茫的环境中,灯塔的黑色轮廓显得格外醒目,呼啸的风声和远处海浪的撞击声增强了孤独的氛围。这一场景展示了灯塔的孤独力量。',
createTime: '2025/10/17 13:41',
duration: 5,
resolution: '1080p',
category: '文生视频',
aspectRatio: '16:9',
videoUrl: '/images/backgrounds/welcome.jpg', // 临时使用图片作为视频
cover: '/images/backgrounds/welcome.jpg'
})
// 根据ID获取视频数据
const getVideoData = (id) => {
// 模拟不同ID对应不同的分类
const videoConfigs = {
'2995000000001': { category: '参考图', title: '图片作品 #1' },
'2995000000002': { category: '参考图', title: '图片作品 #2' },
'2995000000003': { category: '文生视频', title: '视频作品 #3' },
'2995000000004': { category: '图生视频', title: '视频作品 #4' }
}
const config = videoConfigs[id] || videoConfigs['2995000000003']
return {
id: id,
username: 'mingzi_FBx7foZYDS7inL',
title: config.title,
overlayText: config.title,
description: '影片捕捉了暴风雪中的午夜时分,坐落在积雪覆盖的悬崖顶上的孤立灯塔。相机逐渐放大灯塔的灯光,穿透飞舞的雪花,投射出幽幽的光芒。在白茫茫的环境中,灯塔的黑色轮廓显得格外醒目,呼啸的风声和远处海浪的撞击声增强了孤独的氛围。这一场景展示了灯塔的孤独力量。',
createTime: '2025/10/17 13:41',
duration: 5,
resolution: '1080p',
category: config.category,
aspectRatio: '16:9',
videoUrl: '/images/backgrounds/welcome.jpg',
cover: '/images/backgrounds/welcome.jpg'
}
}
// 视频播放控制
const togglePlay = () => {
if (!videoPlayer.value) return
if (isPlaying.value) {
videoPlayer.value.pause()
} else {
videoPlayer.value.play()
}
}
const onVideoLoaded = () => {
duration.value = videoPlayer.value.duration
}
const onTimeUpdate = () => {
currentTime.value = videoPlayer.value.currentTime
progressPercent.value = (currentTime.value / duration.value) * 100
}
const onVideoEnded = () => {
isPlaying.value = false
}
const seekTo = (event) => {
if (!videoPlayer.value) return
const rect = event.currentTarget.getBoundingClientRect()
const clickX = event.clientX - rect.left
const percentage = clickX / rect.width
const newTime = percentage * duration.value
videoPlayer.value.currentTime = newTime
}
const formatTime = (time) => {
const minutes = Math.floor(time / 60)
const seconds = Math.floor(time % 60)
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
}
const toggleFullscreen = () => {
if (!videoPlayer.value) return
if (document.fullscreenElement) {
document.exitFullscreen()
} else {
videoPlayer.value.requestFullscreen()
}
}
// 操作功能
const shareVideo = () => {
ElMessage.info('分享功能开发中')
}
const downloadVideo = () => {
ElMessage.success('开始下载视频')
}
const deleteVideo = async () => {
try {
await ElMessageBox.confirm('确定删除这个视频吗?', '删除确认', {
type: 'warning',
confirmButtonText: '删除',
cancelButtonText: '取消'
})
ElMessage.success('视频已删除')
router.back()
} catch (_) {}
}
const createSimilar = () => {
ElMessage.info('跳转到文生视频创作页面')
// router.push('/create-video')
}
// 导航函数
const goToProfile = () => {
router.push('/profile')
}
const goToSubscription = () => {
router.push('/subscription')
}
const goBack = () => {
router.back()
}
// 监听视频播放状态变化
const handlePlay = () => {
isPlaying.value = true
}
const handlePause = () => {
isPlaying.value = false
}
onMounted(() => {
// 根据路由参数更新视频数据
const videoId = route.params.id
if (videoId) {
videoData.value = getVideoData(videoId)
}
if (videoPlayer.value) {
videoPlayer.value.addEventListener('play', handlePlay)
videoPlayer.value.addEventListener('pause', handlePause)
}
})
onUnmounted(() => {
if (videoPlayer.value) {
videoPlayer.value.removeEventListener('play', handlePlay)
videoPlayer.value.removeEventListener('pause', handlePause)
}
})
</script>
<style scoped>
.video-detail-page {
display: flex;
height: 100vh;
background: #0a0a0a;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* 左侧导航栏 */
.sidebar {
width: 280px;
background: #1a1a1a;
padding: 24px 0;
border-right: 1px solid #1a1a1a;
flex-shrink: 0;
display: block;
position: relative;
}
.logo {
padding: 0 24px 32px;
font-size: 20px;
font-weight: 500;
color: white;
}
.nav-menu {
padding: 0 20px;
}
.nav-item {
display: flex;
align-items: center;
padding: 14px 18px;
margin-bottom: 4px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.nav-item:hover {
background: #2a2a2a;
}
.nav-item.active {
background: #1e3a8a;
}
.nav-item .el-icon {
margin-right: 14px;
font-size: 20px;
}
.nav-item span {
font-size: 15px;
flex: 1;
}
/* 主内容区域 */
.main-content {
flex: 1;
display: flex;
background: #0a0a0a;
overflow: hidden;
}
/* 左侧视频播放器区域 */
.video-player-section {
flex: 2;
display: flex;
align-items: center;
justify-content: center;
background: #000;
position: relative;
}
.video-container {
position: relative;
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
}
.video-player {
width: 100%;
height: 100%;
object-fit: contain;
background: #000;
}
.video-overlay {
position: absolute;
bottom: 80px;
left: 20px;
z-index: 10;
}
.overlay-text {
font-family: 'Brush Script MT', cursive;
font-size: 24px;
color: #8b5cf6;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
font-weight: bold;
}
/* 视频控制栏 */
.video-controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
padding: 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.control-left {
display: flex;
align-items: center;
gap: 15px;
flex: 1;
}
.play-btn {
background: none;
border: none;
color: #fff;
font-size: 20px;
cursor: pointer;
padding: 8px;
border-radius: 50%;
transition: background-color 0.3s;
}
.play-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
.progress-container {
flex: 1;
display: flex;
align-items: center;
gap: 10px;
}
.progress-bar {
flex: 1;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
cursor: pointer;
position: relative;
}
.progress-fill {
height: 100%;
background: #409eff;
border-radius: 2px;
transition: width 0.1s;
}
.time-display {
color: #fff;
font-size: 14px;
white-space: nowrap;
}
.control-right {
display: flex;
align-items: center;
}
.control-btn {
background: none;
border: none;
color: #fff;
font-size: 18px;
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: background-color 0.3s;
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
/* 右上角操作按钮 */
.video-actions {
position: absolute;
top: 20px;
right: 20px;
display: flex;
gap: 10px;
z-index: 10;
}
.action-btn {
background: rgba(0, 0, 0, 0.6);
border: none;
color: #fff;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
}
.action-btn:hover {
background: rgba(0, 0, 0, 0.8);
transform: scale(1.1);
}
.delete-btn:hover {
background: rgba(220, 38, 38, 0.8);
}
/* 右侧详情侧边栏 */
.detail-sidebar {
flex: 1;
background: #1a1a1a;
border-radius: 12px 0 0 0;
padding: 24px;
display: flex;
flex-direction: column;
gap: 24px;
overflow-y: auto;
}
.sidebar-header {
display: flex;
flex-direction: column;
gap: 16px;
}
.user-info {
display: flex;
align-items: center;
gap: 12px;
}
.avatar {
width: 40px;
height: 40px;
background: #409eff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 18px;
}
.username {
font-size: 16px;
font-weight: 500;
color: #fff;
}
.tabs {
display: flex;
gap: 0;
}
.tab {
padding: 8px 16px;
background: transparent;
color: #9ca3af;
cursor: pointer;
border-radius: 6px;
transition: all 0.3s;
font-size: 14px;
}
.tab.active {
background: #409eff;
color: #fff;
}
.tab:hover:not(.active) {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}
.description-section {
display: flex;
flex-direction: column;
gap: 12px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #fff;
margin: 0;
}
.description-text {
font-size: 14px;
line-height: 1.6;
color: #d1d5db;
margin: 0;
}
.metadata-section {
display: flex;
flex-direction: column;
gap: 12px;
}
.metadata-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.label {
font-size: 14px;
color: #9ca3af;
}
.value {
font-size: 14px;
color: #fff;
font-weight: 500;
}
.action-section {
margin-top: auto;
padding-top: 20px;
}
.create-similar-btn {
width: 100%;
background: #409eff;
color: #fff;
border: none;
border-radius: 8px;
padding: 12px 24px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
}
.create-similar-btn:hover {
background: #337ecc;
transform: translateY(-2px);
}
.create-similar-btn:active {
transform: translateY(0);
}
/* 响应式设计 */
@media (max-width: 1024px) {
.video-detail-page {
flex-direction: column;
}
.video-player-section {
flex: 1;
min-height: 50vh;
}
.detail-sidebar {
flex: 1;
border-radius: 0;
}
}
@media (max-width: 768px) {
.video-overlay {
bottom: 60px;
left: 10px;
}
.overlay-text {
font-size: 18px;
}
.video-controls {
padding: 15px;
}
.video-actions {
top: 15px;
right: 15px;
gap: 8px;
}
.action-btn {
width: 35px;
height: 35px;
}
.detail-sidebar {
padding: 16px;
gap: 16px;
}
}
</style>