优化: Safari下载兼容、禁用生产Swagger、前端构建优化移除console、更新COS配置

This commit is contained in:
AIGC Developer
2026-01-05 15:40:28 +08:00
parent 38630dbb66
commit a99cfa28e5
40 changed files with 2550 additions and 1320 deletions

View File

@@ -220,10 +220,10 @@
<div class="title" :title="item.title">{{ item.title }}</div>
<div class="sub">
{{ item.date || t('profile.unknown') }} · {{ item.id }}
<span v-if="item.quality" class="quality-badge" :class="`quality-${(item.quality || '').toLowerCase()}`">
<span v-if="item.quality && item.type !== 'image'" class="quality-badge" :class="`quality-${(item.quality || '').toLowerCase()}`">
{{ formatQuality(item.quality) }}
</span>
· {{ item.sizeText }}
<span v-if="item.sizeText && item.sizeText !== '未知大小'"> · {{ item.sizeText }}</span>
</div>
</div>
<template #footer>
@@ -345,11 +345,11 @@
<span class="label">{{ t('profile.workId') }}</span>
<span class="value">{{ selectedItem.taskId }}</span>
</div>
<div class="metadata-item">
<div class="metadata-item" v-if="selectedItem.type !== 'image'">
<span class="label">{{ t('profile.duration') }}</span>
<span class="value">{{ formatDuration(selectedItem.duration) || '5s' }}</span>
</div>
<div class="metadata-item">
<div class="metadata-item" v-if="selectedItem.type !== 'image'">
<span class="label">{{ t('profile.quality') }}</span>
<span class="value">{{ selectedItem.quality || '1080p' }}</span>
</div>
@@ -511,6 +511,8 @@ const hasMore = ref(true)
const items = ref([])
const showBackToTop = ref(false) // 回到顶部按钮显示状态
const failedUrls = ref(new Set()) // 记录加载失败的URL
const pollingIntervalId = ref(null) // 轮询定时器ID
const POLLING_INTERVAL = 120000 // 2分钟轮询间隔
// 处理URL确保相对路径正确
const processUrl = (url) => {
@@ -564,6 +566,9 @@ const transformWorkData = (work) => {
quality: work.quality || work.resolution || '',
username: work.username || work.user?.username || work.creator || work.author || work.owner || '未知用户',
status: work.status || 'COMPLETED',
uploadedImages: work.uploadedImages || null, // 用户上传的参考图,用于做同款
imagePrompt: work.imagePrompt || null, // 分镜图优化后的提示词
workType: work.workType || '', // 原始作品类型
// overlayText 已移除,前端详情不再显示浮动文本
}
}
@@ -689,6 +694,9 @@ const loadList = async () => {
if (page.value === 1) items.value = []
items.value = items.value.concat(transformedData)
hasMore.value = data.length === pageSize.value
// 检查是否有处理中的任务,如果有则启动轮询
checkAndStartPolling()
} else {
throw new Error(response.data.message || t('profile.loadWorksFailed'))
}
@@ -700,12 +708,80 @@ const loadList = async () => {
}
}
// 检查是否有处理中的任务,如果有则启动轮询
const checkAndStartPolling = () => {
const hasProcessingTasks = items.value.some(
item => item.status === 'PROCESSING' || item.status === 'PENDING'
)
if (hasProcessingTasks && !pollingIntervalId.value) {
console.log('[MyWorks] 检测到处理中的任务启动2分钟轮询')
startPolling()
} else if (!hasProcessingTasks && pollingIntervalId.value) {
console.log('[MyWorks] 没有处理中的任务,停止轮询')
stopPolling()
}
}
// 启动轮询
const startPolling = () => {
if (pollingIntervalId.value) return // 避免重复启动
pollingIntervalId.value = setInterval(async () => {
console.log('[MyWorks] 执行轮询刷新...')
// 静默刷新不显示loading
try {
const response = await getMyWorks({
page: 0,
size: pageSize.value
})
if (response.data.success) {
const data = response.data.data || []
const transformedData = data
.map(transformWorkData)
.filter(work => work.status !== 'FAILED' && work.status !== 'DELETED')
// 更新列表
items.value = transformedData
// 检查是否还需要继续轮询
const hasProcessingTasks = transformedData.some(
item => item.status === 'PROCESSING' || item.status === 'PENDING'
)
if (!hasProcessingTasks) {
console.log('[MyWorks] 所有任务已完成,停止轮询')
stopPolling()
// 刷新用户积分
await userStore.fetchCurrentUser()
}
}
} catch (error) {
console.error('[MyWorks] 轮询刷新失败:', error)
}
}, POLLING_INTERVAL)
}
// 停止轮询
const stopPolling = () => {
if (pollingIntervalId.value) {
clearInterval(pollingIntervalId.value)
pollingIntervalId.value = null
console.log('[MyWorks] 轮询已停止')
}
}
// 筛选后的作品列表
const filteredItems = computed(() => {
let filtered = [...items.value]
// 过滤掉加载失败的作品
// 过滤掉加载失败的作品(但保留 PROCESSING 和 PENDING 状态的作品)
filtered = filtered.filter(item => {
// PROCESSING 和 PENDING 状态的作品始终保留,不受 failedUrls 影响
if (item.status === 'PROCESSING' || item.status === 'PENDING') {
return true
}
const resultUrlFailed = item.resultUrl && failedUrls.value.has(item.resultUrl)
const coverFailed = item.cover && failedUrls.value.has(item.cover)
return !resultUrlFailed && !coverFailed
@@ -1028,22 +1104,41 @@ const createSimilar = (item) => {
taskId: item.taskId,
prompt: item.prompt || '',
aspectRatio: item.aspectRatio || '',
duration: item.duration || ''
duration: item.duration || '',
hdMode: item.quality === 'HD' ? 'true' : 'false'
}
// 添加参考图(图生视频需要,分镜图的 cover 是生成结果不传递)
// 分镜图的 cover 是生成的分镜图,不是用户上传的原始参考图
if (item.category !== '分镜图' && item.cover && item.cover !== '/images/backgrounds/welcome.jpg') {
// 添加参考图(图生视频需要,分镜图/分镜视频的 cover 是生成结果不传递到 referenceImage
if (item.category !== '分镜图' && item.category !== '分镜视频' && item.cover && item.cover !== '/images/backgrounds/welcome.jpg') {
query.referenceImage = item.cover
}
console.log('[做同款] 跳转参数:', query, 'category:', item.category)
// 用户上传的参考图(分镜图/分镜视频做同款需要)
// uploadedImages 现在存储的是 COS URL可以直接通过 URL 传递
if (item.uploadedImages) {
query.uploadedImages = item.uploadedImages
}
console.log('[做同款] 跳转参数:', query, 'category:', item.category, 'workType:', item.workType)
if (item.category === '文生视频') {
router.push({ path: '/text-to-video/create', query })
} else if (item.category === '图生视频') {
router.push({ path: '/image-to-video/create', query })
} else if (item.category === '分镜视频' || item.category === '分镜图') {
} else if (item.category === '分镜' || item.workType === 'STORYBOARD_IMAGE') {
// 分镜图做同款:进入 Step 1生成分镜图使用 imagePrompt
query.step = 'image'
if (item.imagePrompt) {
query.imagePrompt = item.imagePrompt
}
router.push({ path: '/storyboard-video/create', query })
} else if (item.category === '分镜视频') {
// 分镜视频做同款:进入 Step 2生成视频携带已生成的分镜图
query.step = 'video'
// cover 是分镜图thumbnailUrl传递给 Step 2 作为分镜图参考
if (item.cover && item.cover !== '/images/backgrounds/welcome.jpg') {
query.storyboardImage = item.cover
}
router.push({ path: '/storyboard-video/create', query })
} else {
// 默认跳转到文生视频
@@ -1070,36 +1165,20 @@ const download = async (item) => {
console.warn('记录下载次数失败:', err)
}
// 尝试直接从 resultUrl 下载(绕过后端代理)
// 使用兼容 Safari 的下载工具
const videoUrl = item.resultUrl
console.log('直接下载视频URL:', videoUrl)
console.log('下载URL:', videoUrl)
try {
const response = await fetch(videoUrl)
if (!response.ok) {
throw new Error(`下载失败: ${response.status}`)
}
const blob = await response.blob()
console.log('文件大小:', blob.size, 'bytes')
if (blob.size === 0) {
throw new Error('文件内容为空')
}
const blobUrl = window.URL.createObjectURL(blob)
const { downloadFile } = await import('@/utils/download')
const filename = `${item.title || 'work'}_${Date.now()}${item.type === 'video' ? '.mp4' : '.png'}`
const mimeType = item.type === 'video' ? 'video/mp4' : 'image/png'
const a = document.createElement('a')
a.href = blobUrl
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
setTimeout(() => window.URL.revokeObjectURL(blobUrl), 1000)
ElMessage.success(t('works.downloadComplete'))
return
const success = await downloadFile(videoUrl, filename, mimeType)
if (success) {
ElMessage.success(t('works.downloadComplete'))
return
}
} catch (directError) {
console.warn('直接下载失败,尝试后端代理:', directError)
}
@@ -1134,9 +1213,6 @@ const download = async (item) => {
throw new Error('文件内容为空可能URL已过期')
}
// 创建 blob URL
const blobUrl = window.URL.createObjectURL(blob)
// 从响应头获取文件名
const contentDisposition = response.headers.get('content-disposition')
let filename = item.title || 'work'
@@ -1155,13 +1231,10 @@ const download = async (item) => {
console.log('下载文件名:', filename)
// 创建下载链接并触发下载
const a = document.createElement('a')
a.href = blobUrl
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
// 使用兼容工具下载 blob
const blobUrl = window.URL.createObjectURL(blob)
const { downloadFile } = await import('@/utils/download')
await downloadFile(blobUrl, filename, item.type === 'video' ? 'video/mp4' : 'image/png')
// 延迟释放 blob URL
setTimeout(() => window.URL.revokeObjectURL(blobUrl), 1000)
@@ -1560,6 +1633,8 @@ onMounted(async () => {
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
// 清理轮询定时器
stopPolling()
})
// 当页面被激活时(从其他页面返回时)刷新列表