feat: 优化

This commit is contained in:
2025-11-16 22:59:26 +08:00
parent 1540e44288
commit bd2e8b3291
10 changed files with 550 additions and 178 deletions

View File

@@ -78,6 +78,8 @@
import { ref, watch } from 'vue'
import { message } from 'ant-design-vue'
import { UploadOutlined, FileOutlined } from '@ant-design/icons-vue'
import { isVideoFile, extractVideoCover } from '@/utils/video-cover'
import { getFileName, getFileSize, formatFileSize } from '@/utils/file'
const props = defineProps({
visible: {
@@ -94,53 +96,30 @@ const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 数据
const fileList = ref([])
// 文件分类使用默认值不再在UI中显示
const fileCoverMap = ref(new Map())
const DEFAULT_FILE_CATEGORY = 'video'
// 支持的文件类型
const acceptTypes = 'video/*,image/*,audio/*,.mp4,.mov,.avi,.mkv,.jpg,.jpeg,.png,.gif,.webp,.mp3,.wav,.aac'
// 常量
const MAX_FILE_SIZE = 100 * 1024 * 1024 // 100MB
// 监听 visible 变化,重置文件列表
watch(() => props.visible, (newVal) => {
if (!newVal) {
fileList.value = []
fileCoverMap.value.clear()
}
})
// 获取文件名
const getFileName = (fileItem) => {
if (fileItem instanceof File) {
return fileItem.name
}
return fileItem.name || fileItem.file?.name || fileItem.originFileObj?.name || '未知文件'
}
// 获取文件大小
const getFileSize = (fileItem) => {
if (fileItem instanceof File) {
return fileItem.size
}
return fileItem.size || fileItem.file?.size || fileItem.originFileObj?.size || 0
}
// 格式化文件大小
const formatFileSize = (bytes) => {
if (!bytes) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}
// 上传前处理
const handleBeforeUpload = (file) => {
// 检查文件大小100MB
if (file.size > 100 * 1024 * 1024) {
// 检查文件大小
if (file.size > MAX_FILE_SIZE) {
message.warning(`文件 ${file.name} 超过 100MB已跳过`)
return false
}
// 检查是否已存在相同文件(使用当前的 fileList
// 检查是否已存在相同文件
const exists = fileList.value.some(item => {
const itemName = getFileName(item)
const itemSize = getFileSize(item)
@@ -151,24 +130,41 @@ const handleBeforeUpload = (file) => {
return false
}
// 阻止自动上传,文件会通过 change 事件添加到列表
return false
return false // 阻止自动上传
}
// 文件列表变化
const handleFileChange = (info) => {
// 使用 v-model:file-list 后fileList 会自动更新
// 这里只需要处理文件验证和状态
const handleFileChange = async (info) => {
const { file, fileList: newFileList } = info
if (file && file.status !== 'uploading') {
// 确保文件对象正确保存
fileList.value = newFileList.map(item => {
if (!item.file && item.originFileObj) {
item.file = item.originFileObj
fileList.value = newFileList
.map(item => {
if (!item.file && item.originFileObj) {
item.file = item.originFileObj
}
return item
})
.filter(item => item.status !== 'removed')
// 如果是视频文件,自动提取封面
const fileObj = file.file || file.originFileObj || file
if (fileObj instanceof File && isVideoFile(fileObj)) {
const fileKey = file.uid || fileObj.name
if (!fileCoverMap.value.has(fileKey)) {
try {
const coverResult = await extractVideoCover(fileObj, {
maxWidth: 800,
quality: 0.8
})
fileCoverMap.value.set(fileKey, coverResult.base64)
console.log(`[封面提取成功] ${fileObj.name}`)
} catch (error) {
console.warn(`[封面提取失败] ${fileObj.name}:`, error)
}
}
return item
}).filter(item => item.status !== 'removed')
}
}
}
@@ -179,6 +175,9 @@ const handleRemove = (fileItem) => {
(getFileName(item) === getFileName(fileItem))
)
if (index > -1) {
const removed = fileList.value[index]
const fileKey = removed.uid || getFileName(removed)
fileCoverMap.value.delete(fileKey)
fileList.value.splice(index, 1)
}
}
@@ -190,32 +189,26 @@ const handleConfirm = () => {
return
}
// 提取文件对象,优先使用 file其次 originFileObj最后是 item 本身
const files = fileList.value
// 提取文件对象和对应的封面
const filesWithCover = fileList.value
.map(item => {
// 优先使用 file 属性
if (item.file instanceof File) {
return item.file
const fileObj = item.file || item.originFileObj || item
if (!(fileObj instanceof File)) return null
const fileKey = item.uid || fileObj.name
return {
file: fileObj,
coverBase64: fileCoverMap.value.get(fileKey) || null
}
// 其次使用 originFileObj
if (item.originFileObj instanceof File) {
return item.originFileObj
}
// 最后尝试 item 本身(如果是 File 对象)
if (item instanceof File) {
return item
}
return null
})
.filter(file => file instanceof File)
.filter(item => item !== null)
if (files.length === 0) {
if (filesWithCover.length === 0) {
message.error('无法获取文件对象,请重新选择文件')
return
}
// 使用默认分类
emit('confirm', files, DEFAULT_FILE_CATEGORY)
emit('confirm', filesWithCover, DEFAULT_FILE_CATEGORY)
}
// 处理 visible 变化