170 lines
4.0 KiB
TypeScript
170 lines
4.0 KiB
TypeScript
import generateId from '@/utils/generateId'
|
|
import useMiaoFetchApi from '@/hooks/useMiaoFetchApi'
|
|
import VirtualDirectory from './VirtualDirectory'
|
|
|
|
const miaoFetchApi = useMiaoFetchApi()
|
|
|
|
export class UploadQueueItem {
|
|
constructor(
|
|
file: File,
|
|
targetDirectory: VirtualDirectory,
|
|
config?: {
|
|
onSuccess?: () => void
|
|
onFailed?: () => void
|
|
}
|
|
) {
|
|
this.file = file
|
|
this.targetDirectory = targetDirectory
|
|
this.uid = generateId()
|
|
this.status = 'waiting'
|
|
this.process = 0
|
|
this.onSuccess = config && config.onSuccess
|
|
this.onFailed = config && config.onFailed
|
|
}
|
|
uid: number
|
|
file: File
|
|
targetDirectory: VirtualDirectory
|
|
status: 'waiting' | 'uploading' | 'success' | 'failed'
|
|
process: number
|
|
failedReason: unknown | undefined
|
|
|
|
onSuccess?: () => void
|
|
onFailed?: () => void
|
|
|
|
_abort: (() => void | undefined) | undefined
|
|
_status: Promise<any> | undefined
|
|
|
|
/**
|
|
* 开始上传文件
|
|
*/
|
|
startUpload() {
|
|
if (this.status === 'uploading') {
|
|
return
|
|
}
|
|
const { abort, response } = miaoFetchApi.upload(
|
|
this.file,
|
|
this.targetDirectory,
|
|
{
|
|
onProcess: (percent) => {
|
|
this.process = percent
|
|
}
|
|
}
|
|
)
|
|
this._abort = abort
|
|
this._status = response
|
|
this.status = 'uploading'
|
|
response
|
|
.then(() => {
|
|
this.status = 'success'
|
|
this.onSuccess && this.onSuccess()
|
|
this._abort = undefined
|
|
this._status = undefined
|
|
})
|
|
.catch((e) => {
|
|
this.status = 'failed'
|
|
this.failedReason = e
|
|
this.onFailed && this.onFailed()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 终止上传文件
|
|
*/
|
|
abortUpload() {
|
|
this._abort && this._abort()
|
|
this.status = 'failed'
|
|
this.failedReason = 'abort'
|
|
}
|
|
}
|
|
|
|
export default class UploadQueue {
|
|
constructor() {
|
|
this.uid = generateId()
|
|
this.queue = []
|
|
this.mode = 'serial'
|
|
}
|
|
uid: number
|
|
queue: UploadQueueItem[]
|
|
current: UploadQueueItem | undefined
|
|
|
|
// 未来再去实现,目前就是串行的
|
|
// 好像也不用去实现
|
|
mode: 'serial' | 'parallel'
|
|
get status() {
|
|
for (let item of this.queue) {
|
|
if (item.status === 'uploading') {
|
|
return 'busy'
|
|
}
|
|
}
|
|
return 'free'
|
|
}
|
|
|
|
/**
|
|
* 获取下一个需要被上传的item
|
|
*/
|
|
get nextUplpadItem() {
|
|
for (let item of this.queue) {
|
|
if (item.status === 'waiting') {
|
|
return item
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始串行的上传循环
|
|
*/
|
|
loop() {
|
|
if (this.status === 'busy') {
|
|
return
|
|
}
|
|
if (this.nextUplpadItem) {
|
|
this.nextUplpadItem.startUpload()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 向上传文件队列输送一批新的文件
|
|
* @param {any} files 需要上传的文件
|
|
* @param {any} targetDirectory 目标文件夹
|
|
* @param {any} option 每个文件上传完成以后都会触发一遍的回调函数
|
|
* @returns {any}
|
|
*/
|
|
push(
|
|
files: File[],
|
|
targetDirectory: VirtualDirectory,
|
|
option?: {
|
|
/**
|
|
* 每个文件上传完成以后都会触发一遍的回调函数
|
|
*/
|
|
onFinish?: () => void
|
|
/**
|
|
* 是否立即开始上传,若否则按队列顺序一个一个上传
|
|
*/
|
|
immediately?: boolean
|
|
}
|
|
) {
|
|
const onFinish = option?.onFinish
|
|
const immediately = option?.immediately ?? false
|
|
const uploadQueue = files.map((file) => {
|
|
return new UploadQueueItem(file, targetDirectory, {
|
|
onSuccess: () => {
|
|
this.loop()
|
|
onFinish && onFinish()
|
|
},
|
|
onFailed: () => {
|
|
this.loop()
|
|
}
|
|
})
|
|
})
|
|
this.queue.push(...uploadQueue)
|
|
if (!immediately) {
|
|
this.loop()
|
|
} else {
|
|
uploadQueue.forEach((item) => {
|
|
item.startUpload()
|
|
})
|
|
}
|
|
return uploadQueue
|
|
}
|
|
}
|