import { XUPLOADFILE_INFO, XUPLOADFILE_FILE_VALUE, XUPLOADFILE_FILE_INFO, XUPLOADFILE_EVENT_NAME } from "../../interface.uts" import { getUid } from "./xCoreUtil.uts" import { xConfig } from "../../config/xConfig.uts" type CONFIG = { count : number, sourceType : Array, sizeType : Array, hostUrl : string, name : string, header : UTSJSONObject, formData : UTSJSONObject, multipart : boolean, autoUpload : boolean, statusCode : number, compress:boolean, quality:number|null, compressedHeight:number|null, compressedWidth:number|null } /** * 媒体文件上传 */ export class xUploadMedia { model = 'photo' videoOps={ pageOrientation:'auto', albumMode:'system', sourceType:['album', 'camera'] as string[], compressed:true, maxDuration:60, camera:'back' } as UTSJSONObject config = { count: 9, sourceType: ['album', 'camera'], sizeType: ['original', 'compressed'], hostUrl: "", name: "file", header: {} as UTSJSONObject, multipart: false, formData:{} as UTSJSONObject, autoUpload: true, statusCode: 200, compress:true, quality:80, compressedHeight:null, compressedWidth:null } as CONFIG fileList : Array = [] currentIndex = 0; uploading = false; uploadObj = null as null | UploadTask systemError = xConfig.i18n.t("tmui4x.uploadMedia.systemError") limitMaxCount = xConfig.i18n.t("tmui4x.uploadMedia.limitMaxCount") constructor(opts : XUPLOADFILE_INFO = {} as XUPLOADFILE_INFO) { this.chuliConfigArgs(opts) } chooseBefore = (res : string[]) : Promise => Promise.resolve(res) complete = (res : XUPLOADFILE_FILE_INFO[]) : Promise => Promise.resolve(res) change = (res : XUPLOADFILE_FILE_INFO[]) : Promise => Promise.resolve(res) beforeComplete = (res : XUPLOADFILE_FILE_INFO) :XUPLOADFILE_FILE_INFO => res myChangeSync = function (res : XUPLOADFILE_FILE_INFO[]) { } beforeUpload = (res : XUPLOADFILE_FILE_INFO) : Promise => Promise.resolve(res) events:Map Promise> = new Map(); /** * 处理配置参数 * @param opts 配置参数 */ private chuliConfigArgs(opts : XUPLOADFILE_INFO) : CONFIG { this.config = { count: opts.count == null ? this.config.count : opts.count as number, statusCode: opts.statusCode == null ? this.config.statusCode : opts.statusCode as number, sourceType: opts.sourceType == null ? this.config.sourceType : opts.sourceType as string[], sizeType: opts.sizeType == null ? this.config.sizeType : opts.sizeType as string[], hostUrl: opts.hostUrl == null ? this.config.hostUrl : opts.hostUrl as string, name: opts.name == null ? this.config.name : opts.name as string, header: opts.header == null ? this.config.header : opts.header as UTSJSONObject, formData: opts.formData == null ? this.config.formData : opts.formData as UTSJSONObject, multipart: opts.multipart == null ? this.config.multipart : opts.multipart as boolean, autoUpload: opts.autoUpload == null ? this.config.autoUpload : opts.autoUpload as boolean, compress: opts.compress == null ? this.config.compress : opts.compress as boolean, quality: opts.quality == null ? this.config.quality : opts.quality as number, compressedHeight: opts.compressedHeight, compressedWidth: opts.compressedWidth, } as CONFIG return this.config } stop() { if (this.uploadObj == null) return this.currentIndex = 0; this.uploading = false; this.uploadObj!.abort() this.uploadObj = null; console.info("xUploadMedia:中断上传") } chooseMedia() { if (this.fileList.length == this.config.count) { // "已超最大上传数量" uni.showToast({ title: this.limitMaxCount, mask: true, icon: 'none' }) console.warn("xUploadMedia:已经超过最大上传数量") return; } if(this.model == 'photo'){ uni.chooseImage({ count: Math.max(this.config.count - this.fileList.length, 0), sourceType: this.config.sourceType, sizeType: this.config.sizeType, success: (res) => { let temps = [] as UTSJSONObject[] // #ifdef uniVersion >= 4.31 let items = res.tempFiles; for(let i=0;i { this.addNewFile(temps) if (this.config.autoUpload && !this.uploading){ this.start() } }).catch(er => { console.error(er) uni.showModal({ title:this.systemError, content:er as string, showCancel:false }) }) } else { // @ts-ignore let tps = res.tempFilePaths as UTSJSONObject chooseBefore([tps] as UTSJSONObject[]).then(() => { this.addNewFile(temps) if (this.config.autoUpload && !this.uploading){ this.start() } }).catch(er => { console.error(er) uni.showModal({ title:this.systemError, content:er as string, showCancel:false }) }) } }else{ if (Array.isArray(temps)) { this.addNewFile(temps) if (this.config.autoUpload && !this.uploading){ this.start() } }else{ // @ts-ignore let tps = res.tempFilePaths as UTSJSONObject this.addNewFile([tps] as UTSJSONObject[]) if (this.config.autoUpload && !this.uploading){ this.start() } } } }, fail: (err) => { this.chooseBefore([] as string[]) console.warn("xUploadMedia:", err) } }) }else if(this.model == 'video'){ let pageOrientation = this.videoOps.getString('pageOrientation') pageOrientation = pageOrientation==null?'auto':pageOrientation let albumMode = this.videoOps.getString('albumMode') albumMode = albumMode==null?'system':albumMode let sourceType = this.videoOps.getArray('sourceType') sourceType = sourceType==null?(['album', 'camera'] as string[]):sourceType let compressed = this.videoOps.getBoolean('compressed') compressed = compressed==null?true:compressed let maxDuration = this.videoOps.getNumber('maxDuration') maxDuration = maxDuration==null?60:maxDuration let camera = this.videoOps.getString('camera') camera = camera==null?'back':camera uni.chooseVideo({ pageOrientation, albumMode, sourceType, compressed, maxDuration, camera, success: (res) => { let temps = [ {path:res.tempFilePath,size:res.size} as UTSJSONObject ] as UTSJSONObject[] let chooseBefore = this.events.get('chooseBefore') if(chooseBefore!=null){ chooseBefore([res.tempFilePath] as string[]).then(() => { this.addNewFile(temps) if (this.config.autoUpload && !this.uploading){ this.start() } }).catch(er => { console.error(er) uni.showModal({ title:this.systemError, content:er as string, showCancel:false }) }) }else{ this.addNewFile(temps) if (this.config.autoUpload && !this.uploading){ this.start() } } }, fail: (err) => { this.chooseBefore([] as string[]) console.warn("xUploadMedia:", err) } } as ChooseVideoOptions) } } /** * 增加事件监听 * @param eventName 事件名称 * @param callback(res : T) => Promise 回调,注意如果是chooseBefore,外部回调时res是string[],其它为 XUPLOADFILE_FILE_INFO[] */ addListenEvent(eventName : XUPLOADFILE_EVENT_NAME, callback : (res : any) => Promise) { this.events.set(eventName,callback) // if (eventName == 'chooseBefore') { // this.chooseBefore = (res2 : string[]) : Promise => callback(res2 as unknown[] as T) as Promise // } else if (eventName == 'complete') { // this.complete = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise => callback(res2 as unknown[] as T) as Promise // } else if (eventName == 'change') { // this.change = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise => callback(res2 as unknown[] as T) as Promise // } } /** * 增加事件监听 * @param eventName 事件名称 * @param callback(res : T) => Promise 回调,注意如果是chooseBefore,外部回调时res是string[],其它为 XUPLOADFILE_FILE_INFO[] */ addListenEventsss(eventName : XUPLOADFILE_EVENT_NAME, callback : (res : T) => Promise) { if (eventName == 'chooseBefore') { this.chooseBefore = (res2 : string[]) : Promise => callback(res2 as unknown[] as T) as Promise } else if (eventName == 'complete') { this.complete = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise => callback(res2 as unknown[] as T) as Promise } else if (eventName == 'change') { this.change = (res2 : XUPLOADFILE_FILE_INFO[]) : Promise => callback(res2 as unknown[] as T) as Promise } } /** * 设置上传的配置参数 * @param opts 配置参数-XUPLOADFILE_INFO */ setConfig(opts : XUPLOADFILE_INFO) { this.chuliConfigArgs(opts) } setVideoOps(config:UTSJSONObject,model:string){ this.videoOps = config this.model = model; } setChangeSync(callBack : (res : XUPLOADFILE_FILE_INFO[]) => void) { this.myChangeSync = function (res2 : XUPLOADFILE_FILE_INFO[]) { callBack(res2) } } delFile(id : string) : boolean { let index = this.fileList.findIndex((item : XUPLOADFILE_FILE_INFO) : boolean => item.id == id) if (index >= 0) { let item = this.fileList[index]; if (item.status == 1) { item.status = 4 item.statusText = "取消上传" this.stop() } this.fileList.splice(index, 1) this.myChangeSync(this.fileList) return true; } return false } clear(){ this.fileList = [] as XUPLOADFILE_FILE_INFO[] this.myChangeSync(this.fileList) } /** * 添加新选择的未上传文件 */ private addNewFile(files : UTSJSONObject[]) { if (files.length == 0) return; let t = this; // #ifdef APP-ANDROID||APP-IOS if(this.model == 'photo'){ let i=0; function compress(){ if(i>=files.length) return; let item = files[i]! as UTSJSONObject; uni.compressImage({ src: item.getString('path')!, quality: t.config.quality, compressedHeight: t.config.compressedHeight, compressedWidth: t.config.compressedWidth, success: (res:CompressImageSuccess) => { item.set("path",res.tempFilePath) i+=1; compress() }, fail: (err) => { i+=1; compress() } }) } if(this.config.compress){ compress() } } // #endif files.forEach((item : UTSJSONObject) => { let id = getUid(); let name = ""; let size = 0 // #ifdef APP let items = item as UTSJSONObject name = items.getString('path')! size = items.getNumber('size')! // #endif // #ifndef APP name = item.path! size = item.size! // #endif this.fileList.push({ id: id, type: "", size: size, extension: name.substring(name.lastIndexOf(".") + 1), statusText: '待上传', status: 0, path: name, progress: 0, response: "", name: name.substring(name.lastIndexOf("/") + 1), model:this.model } as XUPLOADFILE_FILE_INFO) }) this.myChangeSync(this.fileList) } addFile(files:XUPLOADFILE_FILE_VALUE[]){ let ids = this.fileList.map((el:XUPLOADFILE_FILE_INFO):string=>el.id) let i=0; files.forEach((el:XUPLOADFILE_FILE_VALUE)=>{ let oldId = el.id == null?"":el.id! as string; if(!ids.includes(oldId)){ let id = getUid(); let name = el.url let status = el?.status??2; this.fileList.push({ id: id, type: "", size: 0, extension: name.substring(name.lastIndexOf(".") + 1), statusText: status==2?'上传成功':'待上传', status: el?.status??2, path: name, progress: status==2?100:0, response: el.response == null ?"":el.response! as string, name: name.substring(name.lastIndexOf("/") + 1) } as XUPLOADFILE_FILE_INFO) ++i; } }) if(i>0){ this.myChangeSync(this.fileList) } } _addFilesByself(files:XUPLOADFILE_FILE_INFO[]){ this.fileList = files.slice(0) } /** * 获取待上传的文件数量。 */ private getWaitUploadFilesNumber() : number { let num = 0; this.fileList.forEach((item : XUPLOADFILE_FILE_INFO) => { if (item.status == 0 || item.status == 3 || item.status == 4) { num += 1; } }) return num; } start() { if (this.uploading) return; this.uploading = true; this.currentIndex = 0 this.uploadFile(); } private uploadFile() { // if (!this.config.autoUpload) return; // if (this.fileList.length == 0 || this.getWaitUploadFilesNumber() == 0) { // this.uploadObj = null // this.uploading = false; // console.warn("xUploadMedia:上传结束了") // return; // } if (this.currentIndex >= this.fileList.length) { this.uploadObj = null; this.uploading = false; this.complete(this.fileList) this.myChangeSync(this.fileList) let complete = this.events.get('complete') if(complete!=null){ complete(this.fileList.slice(0)) } return; } this.beforeUpload(this.fileList[this.currentIndex]).then((beforeUploadFileRes:XUPLOADFILE_FILE_INFO)=>{ this.fileList[this.currentIndex] = beforeUploadFileRes; let nowitemStatus = this.fileList[this.currentIndex].status if (nowitemStatus == 1 || nowitemStatus == 2 || nowitemStatus == 5) { this.uploadObj = null this.uploading = false; this.currentIndex += 1; this.uploadFile(); return; } this.fileList[this.currentIndex].status = 1; this.fileList[this.currentIndex].statusText = "上传中"; this.myChangeSync(this.fileList) this.uploadObj = uni.uploadFile({ url: this.config.hostUrl, filePath: this.fileList[this.currentIndex].path, name: this.config.name, formData: this.config.formData, header:this.config.header, success: (uploadFileRes) => { if (uploadFileRes.statusCode != 200) { this.fileList[this.currentIndex].status = 3; this.fileList[this.currentIndex].statusText = "上传失败"; this.currentIndex += 1; this.uploadFile(); this.myChangeSync(this.fileList) return; } this.fileList[this.currentIndex].status = 2; this.fileList[this.currentIndex].response = uploadFileRes.data; this.fileList[this.currentIndex].statusText = "上传成功"; let itemtemp = this.fileList.slice(0)[this.currentIndex] let calllItem = this.beforeComplete(itemtemp) this.fileList[this.currentIndex] = calllItem this.currentIndex += 1; this.uploadFile(); this.myChangeSync(this.fileList) }, fail: (err) => { console.error("上传失败了,请检查配置:",err) this.fileList[this.currentIndex].status = 3; this.fileList[this.currentIndex].statusText = "上传失败"; this.currentIndex += 1; this.uploadFile(); }, complete: () => { this.myChangeSync(this.fileList) } }) this.uploadObj?.onProgressUpdate((res) => { this.fileList[this.currentIndex].progress = res.progress; // if(res.progress == 100){ // this.fileList[this.currentIndex].status = 1; // this.fileList[this.currentIndex].statusText = "上传中"; // }else{ // this.fileList[this.currentIndex].status = 1; // this.fileList[this.currentIndex].statusText = "上传中"; // } this.myChangeSync(this.fileList) }) }) } }