diff --git a/portal-ui/src/components/VideoComposeCard.vue b/portal-ui/src/components/VideoComposeCard.vue index f2ca5e1..9d8245e 100644 --- a/portal-ui/src/components/VideoComposeCard.vue +++ b/portal-ui/src/components/VideoComposeCard.vue @@ -54,16 +54,22 @@
- 参考图({{ mediaList.length }}/{{ maxMediaCount }}) + 参考素材({{ mediaList.length }}/{{ maxMediaCount }})
+
+
+ + + 查询资产 + - 添加图片 + 上传资产
-
点击添加参考图
+
点击上传资产或按 GroupId 查询资产
@@ -73,7 +79,17 @@ class="vg-compose-media-item" :title="item.name || ''">
- + +
-
暂无可引用参考图
+
暂无可引用素材
@@ -136,12 +160,12 @@ + + diff --git a/portal-ui/src/views/AssetManage.vue b/portal-ui/src/views/AssetManage.vue new file mode 100644 index 0000000..988eecc --- /dev/null +++ b/portal-ui/src/views/AssetManage.vue @@ -0,0 +1,496 @@ + + + + + diff --git a/portal-ui/src/views/VideoGen.vue b/portal-ui/src/views/VideoGen.vue index 5679abb..b9af896 100644 --- a/portal-ui/src/views/VideoGen.vue +++ b/portal-ui/src/views/VideoGen.vue @@ -23,10 +23,23 @@ @@ -254,7 +267,7 @@ export default { }, allowedMediaTypes() { if (this.videoMode === 'image-first-frame' || this.videoMode === 'image-first-last-frame') return ['image'] - if (this.videoMode === 'image-reference') return ['image'] + if (this.videoMode === 'image-reference') return ['image', 'video', 'audio'] return ['image', 'video'] }, posterUrl() { @@ -553,22 +566,32 @@ export default { })) }, - getRowReferenceImageUrls(row) { - const urls = [] - if (!row?.videoParams) return urls + getRowReferenceMediaByType(row) { + const images = [] + const videos = [] + const audios = [] + if (!row?.videoParams) return { images, videos, audios } try { const vp = typeof row.videoParams === 'string' ? JSON.parse(row.videoParams) : row.videoParams const content = Array.isArray(vp?.content) ? vp.content : [] for (const item of content) { - if (item?.type !== 'image_url') continue - if (item?.role && item.role !== 'reference_image') continue - const url = item?.image_url?.url - if (url) urls.push(url) + if (item?.type === 'image_url' && (!item.role || item.role === 'reference_image')) { + const url = item?.image_url?.url + if (url) images.push(url) + } + if (item?.type === 'video_url' && item?.role === 'reference_video') { + const url = item?.video_url?.url + if (url) videos.push(url) + } + if (item?.type === 'audio_url' && item?.role === 'reference_audio') { + const url = item?.audio_url?.url + if (url) audios.push(url) + } } } catch (_) { // ignore parse error } - return urls + return { images, videos, audios } }, getRowPromptSegments(row) { @@ -589,25 +612,43 @@ export default { return segs } - // 参考图模式:按 [图n] 替换为对应 reference_image - const refs = this.getRowReferenceImageUrls(row) - if (!refs.length) { + // 参考素材:[图片n][视频n][音频n] 与 content 中同类序号对应;兼容旧 [图n] + const refs = this.getRowReferenceMediaByType(row) + const hasRefPayload = + refs.images.length > 0 || refs.videos.length > 0 || refs.audios.length > 0 + const hasRefTokens = /\[图片\d+\]|\[视频\d+\]|\[音频\d+\]|\[图\d+\]/.test(text) + if (!hasRefPayload && !hasRefTokens) { return [{ type: 'text', text }] } - const tokenReg = /\[图(\d+)\]/g + const tokenReg = /(\[图片(\d+)\]|\[视频(\d+)\]|\[音频(\d+)\]|\[图(\d+)\])/g const segments = [] let last = 0 let m while ((m = tokenReg.exec(text)) !== null) { const token = m[0] - const idx = Number(m[1]) - 1 const start = m.index if (start > last) { segments.push({ type: 'text', text: text.slice(last, start) }) } - const url = idx >= 0 ? refs[idx] : '' - if (url) { - segments.push({ type: 'image', url, token }) + let url = '' + let segType = 'image' + if (m[2] != null) { + url = refs.images[Number(m[2]) - 1] || '' + segType = 'image' + } else if (m[3] != null) { + url = refs.videos[Number(m[3]) - 1] || '' + segType = 'video' + } else if (m[4] != null) { + url = refs.audios[Number(m[4]) - 1] || '' + segType = 'audio' + } else if (m[5] != null) { + url = refs.images[Number(m[5]) - 1] || '' + segType = 'image' + } + if (url && /^asset:\/\//i.test(url)) { + segments.push({ type: 'text', text: token }) + } else if (url) { + segments.push({ type: segType, url, token }) } else { segments.push({ type: 'text', text: token }) } @@ -776,6 +817,10 @@ export default { } params.text = first.text || text params.content = contentItems + const firstPreview = attachments.find((x) => x?.mediaType === 'image' && /^https?:\/\//i.test(String(x?.url || ''))) + if (firstPreview) { + params.referenceUrl = firstPreview.url + } } const urlMap = { @@ -1457,6 +1502,24 @@ export default { border: 1px solid rgba(255, 255, 255, 0.12); } +.vg-chat-inline-ref-video { + display: inline-block; + width: 120px; + max-height: 72px; + vertical-align: middle; + margin: 0 4px; + border-radius: 6px; + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.vg-chat-inline-audio { + display: inline-block; + vertical-align: middle; + margin: 0 4px; + max-width: 200px; + height: 32px; +} + .vg-chat-time { margin-top: 8px; font-size: 12px;