feat: 功能优化

This commit is contained in:
2026-01-17 14:43:42 +08:00
parent 5ed0cfff07
commit fecd47e25d
60 changed files with 3529 additions and 827 deletions

View File

@@ -0,0 +1,169 @@
/**
* 文件上传 Composable Hook
* 支持直传模式和传统上传模式
*/
import { ref, reactive } from 'vue'
import { message } from 'ant-design-vue'
import { MaterialService } from '@/api/material'
/**
* @typedef {Object} UploadOptions
* @property {string} fileCategory - 文件分类video/voice/audio/image
* @property {number|null} groupId - 分组编号(可选,仅素材库模块使用)
* @property {string|null} coverBase64 - 封面base64可选
* @property {Function} onProgress - 进度回调(可选)
* @property {Function} onStart - 开始回调(可选)
* @property {Function} onSuccess - 成功回调(可选)
* @property {Function} onError - 错误回调(可选)
*/
export function useUpload() {
// 上传状态
const state = reactive({
uploading: false,
progress: 0,
status: 'idle', // idle | uploading | success | error
error: null
})
/**
* 重置状态
*/
const reset = () => {
state.uploading = false
state.progress = 0
state.status = 'idle'
state.error = null
}
/**
* 上传文件到OSS直传模式
* @param {File} file - 文件对象
* @param {Object} presignedData - 预签名数据
* @param {Function} onProgress - 进度回调
* @returns {Promise<void>}
*/
const uploadToOSS = async (file, presignedData, onProgress) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
// 监听进度
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable && onProgress) {
const progress = Math.round((event.loaded / event.total) * 100)
onProgress(progress)
}
})
// 监听完成
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve()
} else {
reject(new Error(`上传失败:${xhr.statusText}`))
}
})
// 监听错误
xhr.addEventListener('error', () => {
reject(new Error('网络错误,上传失败'))
})
// 发起PUT请求 - 使用代理路径
const uploadUrl = presignedData.presignedUrl.replace(
'https://muye-ai-chat.oss-cn-hangzhou.aliyuncs.com',
'/oss'
)
xhr.open('PUT', uploadUrl)
if (presignedData.headers && presignedData.headers['Content-Type']) {
xhr.setRequestHeader('Content-Type', presignedData.headers['Content-Type'])
}
xhr.send(file)
})
}
/**
* 主要的上传方法
* @param {File} file - 文件对象
* @param {UploadOptions} options - 上传选项
* @returns {Promise<number>} - 返回文件ID
*/
const upload = async (file, options = {}) => {
const {
fileCategory,
groupId = null,
coverBase64 = null,
onProgress,
onStart,
onSuccess,
onError
} = options
try {
// 设置初始状态
state.uploading = true
state.status = 'uploading'
state.error = null
state.progress = 0
// 通知开始
onStart && onStart()
// 第一步获取预签名URL
const presignedData = await MaterialService.getPresignedUrl({
fileName: file.name,
fileCategory,
groupId,
fileSize: file.size
})
// 第二步直传文件到OSS
await uploadToOSS(file, presignedData.data, (progress) => {
state.progress = progress
onProgress && onProgress(progress)
})
// 第三步:确认上传并保存记录
const completeData = await MaterialService.completeUpload({
fileKey: presignedData.data.fileKey,
fileName: file.name,
fileCategory,
fileSize: file.size,
fileType: file.type,
groupId,
coverBase64,
duration: file.type.startsWith('video/') ? null : undefined // 视频时长由后端处理或前端可选传递
})
// 设置成功状态
state.uploading = false
state.status = 'success'
state.progress = 100
// 通知成功
const fileId = completeData.data?.infraFileId || completeData.data?.userFileId
onSuccess && onSuccess(fileId)
return fileId
} catch (error) {
// 设置错误状态
state.uploading = false
state.status = 'error'
state.error = error.message || '上传失败'
// 通知错误
onError && onError(error)
throw error
}
}
return {
state,
upload,
reset
}
}
export default useUpload