feat: 功能优化
This commit is contained in:
169
frontend/app/web-gold/src/composables/useUpload.js
Normal file
169
frontend/app/web-gold/src/composables/useUpload.js
Normal 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
|
||||
Reference in New Issue
Block a user