diff --git a/portal-ui/src/components/VideoComposeCard.vue b/portal-ui/src/components/VideoComposeCard.vue
index 19525ce..f39dc20 100644
--- a/portal-ui/src/components/VideoComposeCard.vue
+++ b/portal-ui/src/components/VideoComposeCard.vue
@@ -47,6 +47,16 @@
+
@@ -677,12 +687,12 @@ const confirmPickAssets = () => {
}
} else {
const remain = props.maxMediaCount - mediaList.value.length
- if (remain <= 0 && !isReferenceMode) {
+ if (remain <= 0) {
Message.warning(`最多添加 ${props.maxMediaCount} 个参考素材`)
return
}
- const selected = files.slice(0, remain || files.length)
+ const selected = files.slice(0, remain)
const uploadingEntries = []
for (const file of selected) {
@@ -1987,6 +1997,38 @@ defineExpose({
flex-shrink: 0;
}
+.vg-compose-add-tile {
+ cursor: pointer;
+ border: 1px dashed rgba(0, 202, 224, 0.45);
+ background: rgba(0, 202, 224, 0.06);
+ transition: all 0.15s ease-in-out;
+}
+
+.vg-compose-add-tile:hover {
+ border-color: rgba(0, 202, 224, 0.75);
+ background: rgba(0, 202, 224, 0.1);
+}
+
+.vg-compose-add-preview {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+}
+
+.vg-compose-add-icon {
+ font-size: 24px;
+ color: rgba(0, 202, 224, 0.75);
+ line-height: 1;
+}
+
+.vg-compose-add-text {
+ font-size: 11px;
+ color: rgba(255, 255, 255, 0.65);
+ line-height: 1.2;
+}
+
.vg-compose-media-preview {
width: 100%;
height: 100%;
@@ -2042,7 +2084,8 @@ defineExpose({
padding: 8px 10px 10px;
background: rgba(0, 0, 0, 0.18);
box-sizing: border-box;
- overflow: hidden;
+ /* @候选面板 absolute 需要溢出显示;否则会被父级裁切 */
+ overflow: visible;
}
.vg-compose-right-body {
@@ -2079,7 +2122,8 @@ defineExpose({
flex: 1 1 0%;
min-height: 0;
min-width: 0;
- overflow: hidden;
+ /* @候选面板为 absolute,会在内容较多时溢出裁切;需要显示完整 */
+ overflow: visible;
display: flex;
flex-direction: column;
}
@@ -2191,7 +2235,6 @@ defineExpose({
max-height: 72px;
object-fit: cover;
border-radius: 6px;
- vertical-align: middle;
pointer-events: none;
}
@@ -2202,7 +2245,6 @@ defineExpose({
border-radius: 6px;
object-fit: cover;
pointer-events: none;
- vertical-align: middle;
}
.vg-rich-editor :deep(.vg-inline-ref-audio) {
@@ -2255,7 +2297,8 @@ defineExpose({
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
- z-index: 20;
+ /* 层级要足够高,避免 @候选面板在参考图模式下被上层区域遮挡 */
+ z-index: 99999;
}
.vg-mention-item {
diff --git a/portal-ui/src/views/AssetManage.vue b/portal-ui/src/views/AssetManage.vue
index 8c8e5b8..321fedb 100644
--- a/portal-ui/src/views/AssetManage.vue
+++ b/portal-ui/src/views/AssetManage.vue
@@ -182,7 +182,25 @@
-
+
{{ createForm.fileName || '未选择文件' }}
@@ -240,6 +258,7 @@ export default {
name: '',
assetType: 'Image'
},
+ createUploadDragOver: false,
filters: {
groupId: '',
name: '',
@@ -370,10 +389,30 @@ export default {
this.createForm.groupId = gid
this.searchAssets(1)
},
+ triggerCreateFilePicker() {
+ const el = this.$refs.createAssetFileInput
+ if (el && typeof el.click === 'function') el.click()
+ },
+ onCreateUploadDragEnter() {
+ this.createUploadDragOver = true
+ },
+ onCreateUploadDragOver() {
+ this.createUploadDragOver = true
+ },
+ onCreateUploadDragLeave() {
+ this.createUploadDragOver = false
+ },
+ onCreateUploadDrop(e) {
+ this.createUploadDragOver = false
+ const file = e?.dataTransfer?.files?.[0]
+ if (!file) return
+ this.createForm.file = file
+ this.createForm.fileName = file?.name || ''
+ },
onFileChange(e) {
- const file = e?.target?.files?.[0]
- this.createForm.file = file || null
- this.createForm.fileName = file?.name || ''
+ const file = e?.target?.files?.[0]
+ this.createForm.file = file || null
+ this.createForm.fileName = file?.name || ''
},
async createAsset() {
const groupId = String(this.createForm.groupId || '').trim()
@@ -772,6 +811,52 @@ export default {
color: rgba(255, 255, 255, 0.88);
}
+.asset-file-dropzone {
+ width: 100%;
+ max-width: 100%;
+ min-height: 38px;
+ padding: 10px 12px;
+ border-radius: 10px;
+ border: 1px dashed rgba(255, 255, 255, 0.18);
+ background: rgba(0, 0, 0, 0.18);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.15s ease-in-out;
+ box-sizing: border-box;
+}
+
+.asset-file-dropzone--dragover {
+ border-color: rgba(0, 202, 224, 0.65);
+ background: rgba(0, 202, 224, 0.06);
+}
+
+.asset-file-dropzone-content {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.asset-file-dropzone-icon {
+ width: 28px;
+ height: 28px;
+ border-radius: 8px;
+ border: 1px solid rgba(0, 202, 224, 0.35);
+ background: rgba(0, 202, 224, 0.08);
+ color: rgba(0, 202, 224, 0.8);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ flex-shrink: 0;
+}
+
+.asset-file-dropzone-text {
+ font-size: 13px;
+ color: rgba(255, 255, 255, 0.7);
+}
+
.asset-file-hint {
margin-top: 6px;
font-size: 12px;
diff --git a/portal-ui/src/views/VideoGen.vue b/portal-ui/src/views/VideoGen.vue
index ba5e78f..c6d505f 100644
--- a/portal-ui/src/views/VideoGen.vue
+++ b/portal-ui/src/views/VideoGen.vue
@@ -1081,9 +1081,12 @@ export default {
}
params.text = first.text || text
params.content = contentItems
- const firstPreview = attachments.find((x) => x?.mediaType === 'image' && /^https?:\/\//i.test(String(x?.url || '')))
+ // 后端要求:reference_url 需要使用资产标识(asset://assetId)时优先传 asset://
+ // 展示用仍然可以是 https url,但提交 content/reference_url 要与 assetId 关联。
+ const firstPreview = attachments.find((x) => x?.mediaType === 'image')
if (firstPreview) {
- params.referenceUrl = firstPreview.url
+ const aid = String(firstPreview?.assetId || '').trim()
+ params.referenceUrl = aid ? `asset://${aid}` : firstPreview.url
}
}