feat: 视频上传

This commit is contained in:
rd
2025-07-31 18:18:50 +08:00
parent 372f673119
commit 1cb33bd3ad
3 changed files with 157 additions and 33 deletions

View File

@ -1,10 +1,13 @@
<script lang="jsx">
import axios from 'axios';
import { Form, FormItem, Input, Textarea, Upload, Message as AMessage, Button } from '@arco-design/web-vue';
import CommonSelect from '@/components/common-select';
import { VueDraggable } from 'vue-draggable-plus';
import TextOverTips from '@/components/text-over-tips';
import { formatFileSize, getVideoDuration } from '@/utils/tools';
import { getProjectList } from '@/api/all/propertyMarketing';
import { getImagePreSignedUrl, getVideoPreSignedUrl } from '@/api/all/common';
import icon1 from '@/assets/img/creative-generation-workshop/icon-close.png';
@ -13,6 +16,12 @@ const FORM_RULES = {
title: [{ required: true, message: '请输入标题' }],
};
const ENUM_UPLOAD_STATUS = {
DEFAULT: 'default',
UPLOADING: 'uploading',
END: 'end',
};
export default {
name: 'ManuscriptForm',
props: {
@ -30,11 +39,63 @@ export default {
const formRef = ref(null);
const uploadRef = ref(null);
const projects = ref([]);
const uploadStatus = ref(ENUM_UPLOAD_STATUS.DEFAULT);
const isVideo = computed(() => false);
const videoInfo = ref({
name: '',
size: '',
percent: 0,
poster: '',
time: '',
});
function getFileExtension(filename) {
const match = filename.match(/\.([^.]+)$/);
return match ? match[1].toLowerCase() : '';
}
const isVideo = computed(() => true);
const replaceVideo = () => {
console.log('replaceVideo');
function formatDuration(seconds) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
const milliseconds = Math.floor((seconds % 1) * 100);
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}.${milliseconds
.toString()
.padStart(2, '0')}`;
}
const replaceVideo = async (option) => {
try {
const {
fileItem: { file },
} = option;
uploadStatus.value = ENUM_UPLOAD_STATUS.UPLOADING;
// 重置进度为0
videoInfo.value.percent = 0;
videoInfo.value.name = file.name;
videoInfo.value.size = formatFileSize(file.size);
// const duration = getVideoDuration(file);
// videoInfo.value.time = formatDuration(duration);
const response = await getVideoPreSignedUrl({ suffix: getFileExtension(file.name) });
const { file_name, upload_url } = response?.data;
if (!upload_url) {
throw new Error('未能获取有效的预签名上传地址');
}
const blob = new Blob([file], { type: file.type });
const res = await axios.put(upload_url, blob, {
headers: { 'Content-Type': file.type },
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round(progressEvent.progress * 100);
videoInfo.value.percent = percentCompleted;
},
});
props.modelValue.files.push(file_name);
} finally {
uploadStatus.value = ENUM_UPLOAD_STATUS.END;
}
};
// 文件上传处理
const handleUpload = (option) => {
@ -103,7 +164,36 @@ export default {
formRef.value?.clearValidate?.();
};
const renderImage = () => {
const renderVideoUpload = () => {
return (
<Upload
ref={uploadRef}
action="/"
draggable
custom-request={replaceVideo}
accept=".mp4,.webm,.ogg,.mov,.avi,.flv,.wmv,.mkv"
show-file-list={false}
>
{{
'upload-button': () => {
if (uploadStatus.value === ENUM_UPLOAD_STATUS.DEFAULT) {
return (
<div class="upload-box">
<icon-plus size="14" class="mb-16px color-#3C4043" />
<span class="cts !color-#211F24">上传视频</span>
</div>
);
} else {
return <Button type="text">替换视频</Button>;
}
},
}}
</Upload>
);
};
const renderVideo = () => {
const isUploading = uploadStatus.value === ENUM_UPLOAD_STATUS.UPLOADING;
const isEnd = uploadStatus.value === ENUM_UPLOAD_STATUS.END;
return (
<FormItem
field="files"
@ -116,40 +206,42 @@ export default {
),
}}
>
<div class="flex items-center justify-between p-12px rounded-8px bg-#F7F8FA w-784px">
<div class="flex items-center mr-12px">
<img src={''} class="w-80 h-80 object-cover mr-16px rounded-8px" />
<div class="flex flex-col">
<TextOverTips
context={`视频素材标题视频素材标题.mp4`}
class="mb-4px cts !text-14px !lh-22px color-#211F24"
/>
<p>
<span class="cts color-#939499 mr-24px">视频大小483.29kb</span>
<span class="cts color-#939499">视频时长2.73s</span>
</p>
{uploadStatus.value === ENUM_UPLOAD_STATUS.DEFAULT ? (
renderVideoUpload()
) : (
<div class="flex items-center justify-between p-12px rounded-8px bg-#F7F8FA w-784px">
<div class="flex items-center mr-12px">
{isUploading ? (
<div class="w-80px h-80px flex items-center justify-center bg-#fff rounded-8px mr-16px">
<icon-loading size="24" class="color-#B1B2B5" />
</div>
) : (
<img src={''} class="w-80 h-80 object-cover mr-16px rounded-8px" />
)}
<div class="flex flex-col">
<TextOverTips context={videoInfo.value.name} class="mb-4px cts !text-14px !lh-22px color-#211F24" />
{isEnd ? (
<p>
<span class="cts color-#939499 mr-24px">视频大小{videoInfo.value.size}</span>
<span class="cts color-#939499">视频时长2.73s</span>
</p>
) : (
<div class="flex items-center">
<icon-loading size="16" class="color-#6D4CFE mr-8px" />
<span class="cts !color-#6D4CFE mr-4px">上传中</span>
<span class="cts !color-#6D4CFE mr-4px">{videoInfo.value.percent}%</span>
</div>
)}
</div>
</div>
<div>{renderVideoUpload()}</div>
</div>
<div>
<Upload
ref={uploadRef}
action="/"
draggable
custom-request={replaceVideo}
accept=".mp4,.webm,.ogg,.mov,.avi,.flv,.wmv,.mkv"
show-file-list={false}
>
{{
'upload-button': () => <Button type="text">替换视频</Button>,
}}
</Upload>
</div>
</div>
)}
</FormItem>
);
};
const renderVideo = () => {
const renderImage = () => {
return (
<FormItem
field="files"