feat: files接口结构调整对接
This commit is contained in:
@ -26,6 +26,7 @@ export const INITIAL_VIDEO_INFO = {
|
|||||||
name: '',
|
name: '',
|
||||||
size: '',
|
size: '',
|
||||||
percent: 0,
|
percent: 0,
|
||||||
|
duration: 0,
|
||||||
time: '',
|
time: '',
|
||||||
uploadSpeed: '0 KB/s',
|
uploadSpeed: '0 KB/s',
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
@ -76,6 +77,7 @@ export default {
|
|||||||
getVideoInfo(file)
|
getVideoInfo(file)
|
||||||
.then(({ duration, firstFrame }) => {
|
.then(({ duration, firstFrame }) => {
|
||||||
formData.value.videoInfo.poster = firstFrame;
|
formData.value.videoInfo.poster = firstFrame;
|
||||||
|
formData.value.videoInfo.duration = Math.floor(duration);
|
||||||
formData.value.videoInfo.time = formatDuration(duration);
|
formData.value.videoInfo.time = formatDuration(duration);
|
||||||
emit('updateVideoInfo', formData.value.videoInfo);
|
emit('updateVideoInfo', formData.value.videoInfo);
|
||||||
})
|
})
|
||||||
@ -83,6 +85,42 @@ export default {
|
|||||||
console.error('获取视频时长失败:', error);
|
console.error('获取视频时长失败:', error);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const handleUploadProgress = (progressEvent) => {
|
||||||
|
const percentCompleted = Math.round(progressEvent.progress * 100);
|
||||||
|
formData.value.videoInfo.percent = percentCompleted;
|
||||||
|
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const currentLoaded = progressEvent.loaded;
|
||||||
|
const totalSize = progressEvent.total;
|
||||||
|
|
||||||
|
if (formData.value.videoInfo.lastLoaded === 0) {
|
||||||
|
formData.value.videoInfo.lastLoaded = currentLoaded;
|
||||||
|
formData.value.videoInfo.lastTime = currentTime;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeDiff = (currentTime - formData.value.videoInfo.lastTime) / 1000;
|
||||||
|
const bytesDiff = currentLoaded - formData.value.videoInfo.lastLoaded;
|
||||||
|
|
||||||
|
// 避免频繁更新,至少间隔200ms计算一次速率
|
||||||
|
if (timeDiff >= 0.2) {
|
||||||
|
const bytesPerSecond = bytesDiff / timeDiff;
|
||||||
|
formData.value.videoInfo.uploadSpeed = formatUploadSpeed(bytesPerSecond);
|
||||||
|
formData.value.videoInfo.lastLoaded = currentLoaded;
|
||||||
|
formData.value.videoInfo.lastTime = currentTime;
|
||||||
|
|
||||||
|
// 计算预估剩余时间
|
||||||
|
if (totalSize && bytesPerSecond > 0) {
|
||||||
|
const remainingBytes = totalSize - currentLoaded;
|
||||||
|
const remainingSeconds = remainingBytes / bytesPerSecond;
|
||||||
|
formData.value.videoInfo.estimatedTime = formatDuration(remainingSeconds);
|
||||||
|
} else {
|
||||||
|
formData.value.videoInfo.estimatedTime = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('updateVideoInfo', formData.value.videoInfo);
|
||||||
|
};
|
||||||
|
|
||||||
const uploadVideo = async (option) => {
|
const uploadVideo = async (option) => {
|
||||||
try {
|
try {
|
||||||
@ -104,44 +142,12 @@ export default {
|
|||||||
await axios.put(upload_url, blob, {
|
await axios.put(upload_url, blob, {
|
||||||
headers: { 'Content-Type': file.type },
|
headers: { 'Content-Type': file.type },
|
||||||
onUploadProgress: (progressEvent) => {
|
onUploadProgress: (progressEvent) => {
|
||||||
const percentCompleted = Math.round(progressEvent.progress * 100);
|
handleUploadProgress(progressEvent);
|
||||||
formData.value.videoInfo.percent = percentCompleted;
|
|
||||||
|
|
||||||
const currentTime = Date.now();
|
|
||||||
const currentLoaded = progressEvent.loaded;
|
|
||||||
const totalSize = progressEvent.total;
|
|
||||||
|
|
||||||
if (formData.value.videoInfo.lastLoaded === 0) {
|
|
||||||
formData.value.videoInfo.lastLoaded = currentLoaded;
|
|
||||||
formData.value.videoInfo.lastTime = currentTime;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeDiff = (currentTime - formData.value.videoInfo.lastTime) / 1000;
|
|
||||||
const bytesDiff = currentLoaded - formData.value.videoInfo.lastLoaded;
|
|
||||||
|
|
||||||
// 避免频繁更新,至少间隔200ms计算一次速率
|
|
||||||
if (timeDiff >= 0.2) {
|
|
||||||
const bytesPerSecond = bytesDiff / timeDiff;
|
|
||||||
formData.value.videoInfo.uploadSpeed = formatUploadSpeed(bytesPerSecond);
|
|
||||||
formData.value.videoInfo.lastLoaded = currentLoaded;
|
|
||||||
formData.value.videoInfo.lastTime = currentTime;
|
|
||||||
|
|
||||||
// 计算预估剩余时间
|
|
||||||
if (totalSize && bytesPerSecond > 0) {
|
|
||||||
const remainingBytes = totalSize - currentLoaded;
|
|
||||||
const remainingSeconds = remainingBytes / bytesPerSecond;
|
|
||||||
formData.value.videoInfo.estimatedTime = formatDuration(remainingSeconds);
|
|
||||||
} else {
|
|
||||||
formData.value.videoInfo.estimatedTime = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('updateVideoInfo', formData.value.videoInfo);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
formData.value.files.push(file_url);
|
const { name, duration, size } = formData.value.videoInfo;
|
||||||
|
formData.value.files.push({ url: file_url, name, duration, size });
|
||||||
onChange();
|
onChange();
|
||||||
} finally {
|
} finally {
|
||||||
formData.value.videoInfo.uploadStatus = ENUM_UPLOAD_STATUS.END;
|
formData.value.videoInfo.uploadStatus = ENUM_UPLOAD_STATUS.END;
|
||||||
@ -158,50 +164,29 @@ export default {
|
|||||||
fileItem: { file },
|
fileItem: { file },
|
||||||
} = option;
|
} = option;
|
||||||
|
|
||||||
// 验证文件大小 (5MB)
|
|
||||||
// const maxSize = 5 * 1024 * 1024;
|
|
||||||
// if (file.size > maxSize) {
|
|
||||||
// AMessage.error('图片大小不能超过5MB!');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 验证文件数量
|
// 验证文件数量
|
||||||
if (formData.value.files?.length >= 18) {
|
if (formData.value.files?.length >= 18) {
|
||||||
AMessage.error('最多只能上传18张图片!');
|
AMessage.error('最多只能上传18张图片!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { name, size, type } = file;
|
||||||
const response = await getImagePreSignedUrl({ suffix: getFileExtension(file.name) });
|
const response = await getImagePreSignedUrl({ suffix: getFileExtension(name) });
|
||||||
const { file_name, upload_url, file_url } = response?.data;
|
const { file_name, upload_url, file_url } = response?.data;
|
||||||
|
|
||||||
const blob = new Blob([file], { type: file.type });
|
const blob = new Blob([file], { type });
|
||||||
await axios.put(upload_url, blob, {
|
await axios.put(upload_url, blob, {
|
||||||
headers: { 'Content-Type': file.type },
|
headers: { 'Content-Type': type },
|
||||||
});
|
});
|
||||||
|
|
||||||
formData.value.files.push(file_url);
|
formData.value.files.push({ url: file_url, name, size });
|
||||||
onChange();
|
onChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除文件
|
|
||||||
const handleDeleteFile = (index) => {
|
const handleDeleteFile = (index) => {
|
||||||
formData.value.files.splice(index, 1);
|
formData.value.files.splice(index, 1);
|
||||||
onChange();
|
onChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
// // 获取项目列表
|
|
||||||
// const getProjects = async () => {
|
|
||||||
// try {
|
|
||||||
// const { code, data } = await getProjectList();
|
|
||||||
// if (code === 200) {
|
|
||||||
// projects.value = data;
|
|
||||||
// }
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error('获取项目列表失败:', error);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// 表单验证
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
formRef.value?.validate((errors) => {
|
formRef.value?.validate((errors) => {
|
||||||
@ -214,7 +199,6 @@ export default {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置表单
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {};
|
formData.value = {};
|
||||||
formRef.value?.resetFields?.();
|
formRef.value?.resetFields?.();
|
||||||
@ -329,7 +313,7 @@ export default {
|
|||||||
{formData.value.files?.map((file, index) => (
|
{formData.value.files?.map((file, index) => (
|
||||||
<div key={index} class="group relative cursor-move overflow-visible py-8px pr-8px">
|
<div key={index} class="group relative cursor-move overflow-visible py-8px pr-8px">
|
||||||
<div class="group-container relative rounded-8px w-100px h-100px">
|
<div class="group-container relative rounded-8px w-100px h-100px">
|
||||||
<img src={file} class=" object-cover w-full h-full border-1px border-#E6E6E8 rounded-8px" />
|
<img src={file.url} class=" object-cover w-full h-full border-1px border-#E6E6E8 rounded-8px" />
|
||||||
<icon-close-circle-fill
|
<icon-close-circle-fill
|
||||||
size={16}
|
size={16}
|
||||||
class="absolute top--8px right--8px cursor-pointer hidden color-#939499 hidden group-hover:block z-50"
|
class="absolute top--8px right--8px cursor-pointer hidden color-#939499 hidden group-hover:block z-50"
|
||||||
|
|||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
const initData = () => {
|
const initData = () => {
|
||||||
const [fileOne, ...fileOthers] = dataSource.value.files ?? [];
|
const [fileOne, ...fileOthers] = dataSource.value.files ?? [];
|
||||||
if (isVideo.value) {
|
if (isVideo.value) {
|
||||||
videoUrl.value = fileOne;
|
videoUrl.value = fileOne.url;
|
||||||
coverImageUrl.value = convertVideoUrlToCoverUrl(fileOne);
|
coverImageUrl.value = convertVideoUrlToCoverUrl(fileOne.url);
|
||||||
} else {
|
} else {
|
||||||
coverImageUrl.value = fileOne;
|
coverImageUrl.value = fileOne.url;
|
||||||
images.value = fileOthers;
|
images.value = fileOthers;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -172,7 +172,7 @@ export default {
|
|||||||
<div class="desc-img-wrap mt-40px">
|
<div class="desc-img-wrap mt-40px">
|
||||||
{images.value.map((item, index) => (
|
{images.value.map((item, index) => (
|
||||||
<div class="desc-img-box" key={index}>
|
<div class="desc-img-box" key={index}>
|
||||||
<img src={item} class="w-100% h-100% object-contain" />
|
<img src={item.url} class="w-100% h-100% object-contain" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,15 +4,7 @@ import EditForm, { ENUM_UPLOAD_STATUS, INITIAL_VIDEO_INFO } from '../components/
|
|||||||
import CancelEditModal from './cancel-edit-modal.vue';
|
import CancelEditModal from './cancel-edit-modal.vue';
|
||||||
import { getWorksDetail, putWorksUpdate } from '@/api/all/generationWorkshop';
|
import { getWorksDetail, putWorksUpdate } from '@/api/all/generationWorkshop';
|
||||||
import { EnumManuscriptType } from '@/views/creative-generation-workshop/manuscript/list/constants.ts';
|
import { EnumManuscriptType } from '@/views/creative-generation-workshop/manuscript/list/constants.ts';
|
||||||
|
import { formatDuration, formatFileSize, convertVideoUrlToCoverUrl } from '@/utils/tools';
|
||||||
// const INITIAL_DATA = {
|
|
||||||
// title: '',
|
|
||||||
// content: '',
|
|
||||||
// type: EnumManuscriptType.Video,
|
|
||||||
// // project_ids: [],
|
|
||||||
// files: [],
|
|
||||||
// videoInfo: cloneDeep(INITIAL_VIDEO_INFO),
|
|
||||||
// };
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -32,7 +24,7 @@ export default {
|
|||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
const isModified = !isEqual(dataSource.value, remoteDataSource.value);
|
const isModified = !isEqual(dataSource.value, remoteDataSource.value);
|
||||||
if (isModified && isSaved.value) {
|
if (isModified && !isSaved.value) {
|
||||||
cancelEditModal.value?.open();
|
cancelEditModal.value?.open();
|
||||||
} else {
|
} else {
|
||||||
onBack();
|
onBack();
|
||||||
@ -60,25 +52,27 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const init = () => {
|
|
||||||
dataSource.value = cloneDeep(INITIAL_DATA);
|
|
||||||
remoteDataSource.value = cloneDeep(INITIAL_DATA);
|
|
||||||
};
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const { code, data } = await getWorksDetail(workId.value);
|
const { code, data } = await getWorksDetail(workId.value);
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
const { type, files } = data;
|
const { type, files } = data;
|
||||||
const _data = { ...data, videoInfo: cloneDeep(INITIAL_VIDEO_INFO) };
|
const _data = { ...data, videoInfo: cloneDeep(INITIAL_VIDEO_INFO) };
|
||||||
|
|
||||||
|
// 初始化视频数据
|
||||||
if (type === EnumManuscriptType.Video && files.length) {
|
if (type === EnumManuscriptType.Video && files.length) {
|
||||||
_data.videoInfo.uploadStatus = 'end';
|
_data.videoInfo.uploadStatus = 'end';
|
||||||
|
const { name, size, duration, url } = files[0];
|
||||||
|
_data.videoInfo.name = name;
|
||||||
|
_data.videoInfo.size = formatFileSize(size);
|
||||||
|
_data.videoInfo.time = formatDuration(duration);
|
||||||
|
_data.videoInfo.poster = convertVideoUrlToCoverUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataSource.value = cloneDeep(_data);
|
dataSource.value = cloneDeep(_data);
|
||||||
remoteDataSource.value = cloneDeep(_data);
|
remoteDataSource.value = cloneDeep(_data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onChange = (val) => {
|
const onChange = (val) => {
|
||||||
console.log('onChange', val);
|
|
||||||
dataSource.value = val;
|
dataSource.value = val;
|
||||||
};
|
};
|
||||||
const onUpdateVideoInfo = (newVideoInfo) => {
|
const onUpdateVideoInfo = (newVideoInfo) => {
|
||||||
@ -88,7 +82,6 @@ export default {
|
|||||||
router.push({ name: 'ManuscriptList' });
|
router.push({ name: 'ManuscriptList' });
|
||||||
};
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// init();
|
|
||||||
workId && getData();
|
workId && getData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user