From df3bcf67d650a6e9853ec1cc20d40dc402ca47d3 Mon Sep 17 00:00:00 2001 From: old burden Date: Wed, 8 Apr 2026 13:33:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- portal-ui/src/components/VideoComposeCard.vue | 170 +++++++++++++++++- portal-ui/src/views/ThirdPartyAsset.vue | 1 + 2 files changed, 168 insertions(+), 3 deletions(-) diff --git a/portal-ui/src/components/VideoComposeCard.vue b/portal-ui/src/components/VideoComposeCard.vue index 47bde21..85425c4 100644 --- a/portal-ui/src/components/VideoComposeCard.vue +++ b/portal-ui/src/components/VideoComposeCard.vue @@ -235,6 +235,42 @@ + +
+
+ 类型选择: + + 图片 + 视频 + 音频 + +
+
添加素材,拖拽或者点击上传
+ +
+ + 提交审核 + + 取消 +
+
+
@@ -348,6 +384,11 @@ const assetLoading = ref(false) const assetPickerVisible = ref(false) const assetQueryResults = ref([]) const assetPickerSelectedKeys = ref([]) +const createAssetDialogVisible = ref(false) +const createAssetMediaType = ref('image') +const createAssetUploaderKey = ref(0) +const createAssetPendingUrls = ref([]) +const createAssetSubmitting = ref(false) /** 参考图区拖拽高亮(嵌套 dragenter/leave 计数) */ const referenceDragDepth = ref(0) /** 仅「上传资产」按钮为 true;左侧直接上传/拖拽/粘贴为 false */ @@ -429,9 +470,9 @@ const openReferenceDirectUpload = () => { /** 仅「上传资产」:/api/cos/upload(pathPrefix=asset) + /api/tos/asset,与三方素材管理一致 */ const openFilePickerForAssetUpload = () => { - createAssetIntent.value = true - currentUploadIndex.value = -1 - fileInputRef.value?.click() + createAssetMediaType.value = 'image' + clearCreateAssetUploads() + createAssetDialogVisible.value = true } const openFilePicker = () => { @@ -449,6 +490,100 @@ const openFilePickerFor = (index) => { fileInputRef.value?.click() } +const uploadAcceptByType = (mediaType) => { + if (mediaType === 'audio') return 'audio/*' + if (mediaType === 'video') return 'video/*' + return 'image/*' +} + +const mediaDataKeyByType = (mediaType) => { + if (mediaType === 'audio') return 'audios' + if (mediaType === 'video') return 'videos' + return 'images' +} + +const clearCreateAssetUploads = () => { + createAssetPendingUrls.value = [] + createAssetUploaderKey.value += 1 +} + +const onCreateAssetTypeChange = () => { + clearCreateAssetUploads() +} + +const cancelCreateAssetDialog = () => { + clearCreateAssetUploads() + createAssetDialogVisible.value = false +} + +const customCreateAssetRequest = async (option) => { + const { onProgress, onError, onSuccess, fileItem } = option || {} + try { + const form = new FormData() + form.append('file', fileItem.file) + form.append('pathPrefix', 'asset') + const res = await request({ + url: 'api/cos/upload', + method: 'post', + data: form, + headers: { + 'Content-Type': 'multipart/form-data;boundary=' + new Date().getTime() + }, + onUploadProgress: (evt) => { + if (evt.total && onProgress) onProgress(evt.loaded / evt.total, evt) + } + }) + if (res && res.code === 200 && res.url) { + createAssetPendingUrls.value.push(res.url) + if (onSuccess) onSuccess(res) + } else { + const msg = (res && res.msg) || '上传失败' + throw new Error(msg) + } + } catch (err) { + const msg = err?.message || '上传失败' + Message.error(msg) + if (onError) onError(err) + } +} + +const onCreateAssetUploadChange = (_list, fileItem) => { + const st = fileItem && fileItem.status + if (st === 'removed' && fileItem.response && fileItem.response.url) { + const u = fileItem.response.url + const idx = createAssetPendingUrls.value.findIndex((x) => x === u) + if (idx >= 0) createAssetPendingUrls.value.splice(idx, 1) + } +} + +const submitCreateAssetModeration = async () => { + if (!createAssetPendingUrls.value.length) { + Message.warning('请先上传素材') + return + } + const payload = { + [mediaDataKeyByType(createAssetMediaType.value)]: [...createAssetPendingUrls.value] + } + createAssetSubmitting.value = true + try { + const res = await request({ + url: 'api/tos/asset', + method: 'post', + data: payload + }) + if (res.code !== 200) { + throw new Error(res.msg || '提交失败') + } + Message.success('已提交审核') + clearCreateAssetUploads() + createAssetDialogVisible.value = false + } catch (err) { + Message.error(err?.message || '提交失败') + } finally { + createAssetSubmitting.value = false + } +} + const acceptAttr = computed(() => { const types = new Set(props.allowedMediaTypes || []) const hasI = types.has('image') @@ -1972,6 +2107,35 @@ defineExpose({ padding: 8px 0; } +.vg-create-asset-block { + padding: 2px 0; +} + +.vg-create-asset-type { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 12px; +} + +.vg-create-asset-type-label { + color: rgba(255, 255, 255, 0.85); + font-size: 13px; +} + +.vg-create-asset-label { + margin-bottom: 10px; + color: rgba(255, 255, 255, 0.75); + font-size: 14px; +} + +.vg-create-asset-actions { + margin-top: 14px; + display: flex; + gap: 12px; + flex-wrap: wrap; +} + .hidden-input { display: none; } diff --git a/portal-ui/src/views/ThirdPartyAsset.vue b/portal-ui/src/views/ThirdPartyAsset.vue index bf92e63..429b4b1 100644 --- a/portal-ui/src/views/ThirdPartyAsset.vue +++ b/portal-ui/src/views/ThirdPartyAsset.vue @@ -302,6 +302,7 @@ export default {