<template> <div class="app-data-upload-view" element-loading-background="rgba(57, 57, 57, 0.2)"> <input ref="inputUpLoad" type="file" style="display: none" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @change="fileChange" /> <div class="main-content"> <template v-if="asyncActionState"> <div class="data-info-container"> <div class="message-container"> <div class="upload-progress-container"> <el-progress :percentage="dataProgress"></el-progress> <div>{{ dataProgressText }}</div> </div> </div> </div> </template> <template v-else> <div v-if="!selectedFile" class="upload-container" @click="handleUpLoad"> <img class="icon-import" src="@/assets/img/icon-import.svg" /> <span class="select-file-text">{{ $t('components.appdatauploadview.selectfile') }}</span> </div> <div v-else class="data-info-container"> <div v-if="!isUploaded" class="message-container"> <div class="success-list" v-if="!isUploading"> {{ $t('components.appdatauploadview.selectfilesucess') }} </div> <div class="success-list" v-if="isUploading"> <div class="upload-progress-container"> <!-- <el-progress :percentage="uploadedProgress"></el-progress> --> <div>正在上传文件,请稍候</div> </div> </div> </div> <div v-else class="message-container"> <div class="result-list"> <ul v-if="Object.keys(responseResult).length > 0"> <template v-if="ignoreError"> <li class="title"> <span>{{ $t('components.appdatauploadview.importresult') }}</span> </li> <li> <span> {{ $t('components.appdatauploadview.totaldata') }} {{ responseResult.total }} {{ $t('components.appdatauploadview.total') }},{{ $t('app.commonwords.wrong') }}[{{ responseResult.error }}],{{ $t('app.commonwords.success') }}[{{ responseResult.success }}] </span> </li> </template> <template v-if="responseResult.errorInfos && responseResult.errorInfos.length !== 0"> <li class="title"> <span>{{ $t('components.appdatauploadview.errorinfo') }}</span> </li> <li class="error-item" v-for="(item, index) in responseResult.errorInfos" :key="index" > <span v-if="item.index" >{{ $t('components.appdatauploadview.start') }} {{ item.index }} {{ $t('components.appdatauploadview.row') }}:</span ><span v-html="item.errorinfo"></span> </li> </template> </ul> </div> </div> </div> </template> </div> <el-row class="second-content"> <el-col> <div class="import-template-message">{{ $t('components.appdatauploadview.datatemplatemessage') }}</div> <div class="import-template"> <img class="icon-link" src="@/assets/img/icon-link.svg" /> <span style="cursor: pointer" @click="downloadTemp"> {{ viewparams.appDeLogicName + $t('components.appdatauploadview.datatemplate') }}</span > </div> </el-col> <div class="import-template-download-info" v-if="isUploaded && responseResult.errorfile" @click="downloadFeedbackMsg" > {{ $t('components.appdatauploadview.downloadinfo') }} </div> </el-row> <el-row class="button-container"> <el-button @click="handleCancel">{{ asyncActionState ? '确认' : $t('components.appdatauploadview.cancel') }}</el-button> <el-button v-if="!asyncActionState" :disabled="!selectedFile" :loading="isUploading" type="primary" @click="uploadServer" >{{ $t('components.appdatauploadview.uploadserver') }}</el-button > </el-row> </div> </template> <script lang="ts"> import axios from 'axios'; import { AppServiceBase, Util } from 'ibiz-core'; import { ActionState, AppCenterService, AppNoticeService, NotificationFactory, NotificationItem, SubType, } from 'ibiz-vue'; import { Subscription } from 'rxjs'; import { Vue, Component, Prop, Watch } from 'vue-property-decorator'; @Component({}) export default class AppDataUploadView extends Vue { /** * 传入视图参数 * * @type {string} * @memberof AppDataUploadView */ @Prop() public dynamicProps!: string; /** * 视图参数 * * @type {*} * @memberof AppDataUploadView */ public viewparams: any = {}; /** * 视图上下文 * * @type {*} * @memberof AppDataUploadView */ public viewdata: any = {}; /** * 是否忽略错误 * * @type {boolean} * @memberof AppDataUploadView */ public ignoreError: boolean = false; /** * 选择文件数据 * * @type {*} * @memberof AppDataUploadView */ public selectedFile: any | null = null; /** * 是否上传完成 * * @type {boolean} * @memberof AppDataUploadView */ public isUploaded: boolean = false; /** * 上传进度 * * @type {number} * @memberof AppDataUploadView */ public uploadedProgress: number = 0; /** * 数据导入进度 * * @type {number} * @memberof AppDataUploadView */ public dataProgress: number = 0; /** * 数据导入进度文本 * * @type {string} * @memberof AppDataUploadView */ public dataProgressText: string = ''; /** * 是否上传过程中 * * @type {boolean} * @memberof AppDataUploadView */ public isUploading: boolean = false; /** * 导入结果集合 * * @type {Array<*>} * @memberof AppDataUploadView */ public responseResult: any = {}; /** * 是否启用异步导入 * * @type {boolean} * @memberof AppDataUploadView */ public enableAsyncImport: boolean = false; /** * 异步导入状态(true:当前环境存在异步作业/false:当前环境不存在异步作业) * * @type {boolean} * @memberof AppDataUploadView */ public asyncActionState: boolean = false; /** * 应用状态事件 * * @type {Subscription | undefined} * @memberof AppDataUploadView */ public appStateEvent: Subscription | undefined; /** * vue 生命周期 * * @memberof Breadcrumb */ created() { const Environment = AppServiceBase.getInstance().getAppEnvironment(); this.enableAsyncImport = Environment.enableAsyncImport; if (this.enableAsyncImport) { this.initAsyncActionState(); this.handleAsyncAction(); } } /** * 组件销毁 */ destroyed() { if (this.appStateEvent) { this.appStateEvent.unsubscribe(); } } /** * 视图参数变化 * * @param {*} newVal * @param {*} oldVal * @memberof AppDataUploadView */ @Watch('dynamicProps', { immediate: true, deep: true }) onParamData(newVal: any, oldVal: any) { if (newVal) { this.viewparams = eval('(' + newVal.viewparam + ')'); this.viewdata = eval('(' + newVal.viewdata + ')'); this.ignoreError = this.viewparams?.ignoreError; } } /** * 初始化异步消息状态 * * @memberof AppDataUploadView */ public initAsyncActionState() { const asyncActionService = NotificationFactory.getInstance().getSubInstance(SubType.ASYNCACTION); if (asyncActionService) { const items = asyncActionService.getItems(true); if (items && items.length > 0) { const targetItem = items.find((item: NotificationItem) => { return ( this.viewparams.serviceName.toUpperCase() === item.actionparam && this.viewparams.importId === item.actionparam2 && item.state === ActionState.CREATING ); }); if (targetItem) { this.asyncActionState = true; this.dataProgress = targetItem.completionrate ? targetItem.completionrate : 0; this.dataProgressText = '正在导入数据,请稍候'; } } } } /** * 处理异步消息 * * @memberof AppDataUploadView */ public handleAsyncAction() { this.appStateEvent = AppCenterService.getMessageCenter().subscribe( ({ name, action, data }: { name: string; action: string; data: any }) => { if (!Object.is(name, 'AsyncAction')) { return; } if (Object.is(action, 'AddItem')) { if ( Object.is(this.viewparams.importId, data.actionparam2) && Object.is(this.viewparams.serviceName.toUpperCase(), data.actionparam) ) { if (data.state === ActionState.CREATING) { this.dataProgress = data.completionrate ? data.completionrate : 0; this.dataProgressText = '正在导入数据,请稍候'; } if (data.state === ActionState.CREATED) { const actionresult = JSON.parse(data.actionresult); this.dataProgress = 100; this.dataProgressText = `数据导入成功,导入数据共计${actionresult.total}条,成功导入${actionresult.success}条`; } } } }, ); } /** * 选择文件 * * @memberof AppDataUploadView */ public handleUpLoad() { (this.$refs.inputUpLoad as any).click(); } /** * 取消 * * @memberof AppDataUploadView */ public handleCancel() { this.$emit('close', []); } /** * 文件数据变化 * * @memberof AppDataUploadView */ public fileChange($event: any) { let obj = $event.target || $event.srcElement; if (!obj.files) { return; } this.selectedFile = obj.files?.[0]; } /** * 设置UI状态 * * @memberof AppDataUploadView */ public setUIState(uploadedProgress: number, isUploading: boolean, isUploaded: boolean, result: any = {}) { this.uploadedProgress = uploadedProgress; this.isUploading = isUploading; this.isUploaded = isUploaded; this.responseResult = result; } /** * 下载导入数据模板 * * @memberof AppDataUploadView */ public downloadTemp() { let requestUrl: string = ''; if ( this.viewdata && this.viewdata.srfparentkey && this.viewdata.srfparentdename && this.viewdata.srfparentdename !== this.viewdata.appEntityName ) { requestUrl += `/${Util.srfpluralize(this.viewdata.srfparentdename)}/${this.viewdata.srfparentkey}`; } requestUrl += `/${Util.srfpluralize(this.viewparams.serviceName)}/importtemplate`; if (this.viewparams.importId) { requestUrl += `?srfimporttag=${this.viewparams.importId}`; } axios({ url: requestUrl, method: 'get', responseType: 'blob', }).then((response: any) => { if (response.status == 200) { let fileName = response.headers['content-disposition'] .split(';') .find((str: string) => str.indexOf('filename=') != -1) ?.slice(9); fileName = decodeURIComponent(fileName); let blob = new Blob([response.data], { type: 'application/vnd.ms-excel' }); let elink = document.createElement('a'); elink.download = fileName; elink.style.display = 'none'; elink.href = URL.createObjectURL(blob); document.body.appendChild(elink); elink.click(); URL.revokeObjectURL(elink.href); // 释放URL 对象 document.body.removeChild(elink); } }); } /** * 下载导入反馈信息 * * @memberof AppDataUploadView */ public downloadFeedbackMsg() { if (!this.responseResult || !this.responseResult.errorfile || !this.responseResult.errorfile.fileid) { this.$throw(this.$t('components.appdatauploadview.downloaderror')); return; } let downloadUrl: string = `${AppServiceBase.getInstance().getAppEnvironment().ExportFile}/${ this.responseResult.errorfile.folder }/${this.responseResult.errorfile.fileid}`; const headers = {}; axios({ method: 'get', url: downloadUrl, headers: headers, responseType: 'blob', }) .then((response: any) => { if (!response || response.status != 200) { this.$throw(this.$t('components.appdatauploadview.downloaderror')); return; } // 请求成功,后台返回的是一个文件流 if (response.data) { // 获取文件名 const filename = `导入错误${this.responseResult.errorfile.fileid}.xlsx`; let filetype = this.calcFilemime('xlsx'); // 用blob对象获取文件流 let blob = new Blob([response.data], { type: filetype }); // 通过文件流创建下载链接 var href = URL.createObjectURL(blob); // 创建一个a元素并设置相关属性 let a = document.createElement('a'); a.href = href; a.download = filename; // 添加a元素到当前网页 document.body.appendChild(a); // 触发a元素的点击事件,实现下载 a.click(); // 从当前网页移除a元素 document.body.removeChild(a); // 释放blob对象 URL.revokeObjectURL(href); } else { this.$throw(this.$t('components.appfileupload.downloaderror')); } }) .catch((error: any) => { console.error(error); }); } /** * 计算文件mime类型 * * @param filetype 文件后缀 * @memberof AppDataUploadView */ public calcFilemime(filetype: string): string { let mime = 'application/vnd.ms-excel'; switch (filetype) { case '.xlsx': mime = 'application/vnd.ms-excel'; break; case '.wps': mime = 'application/kswps'; break; case '.doc': mime = 'application/msword'; break; case '.docx': mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; break; case '.txt': mime = 'text/plain'; break; case '.zip': mime = 'application/zip'; break; case '.png': mime = 'image/png'; break; case '.gif': mime = 'image/gif'; break; case '.jpeg': mime = 'image/jpeg'; break; case '.jpg': mime = 'image/jpeg'; break; case '.rtf': mime = 'application/rtf'; break; case '.avi': mime = 'video/x-msvideo'; break; case '.gz': mime = 'application/x-gzip'; break; case '.tar': mime = 'application/x-tar'; break; } return mime; } /** * 上传服务器 * * @memberof AppDataUploadView */ public uploadServer() { if (!this.selectedFile) { return; } try { let requestUrl: string = ''; this.setUIState(0, true, false); if (this.viewdata && this.viewdata.srfparentkey && this.viewdata.srfparentdename) { requestUrl += `/${Util.srfpluralize(this.viewdata.srfparentdename)}/${this.viewdata.srfparentkey}`; } if (this.enableAsyncImport) { requestUrl += `/${Util.srfpluralize(this.viewparams.serviceName)}/asyncimportdata2`; } else { requestUrl += `/${Util.srfpluralize(this.viewparams.serviceName)}/importdata2`; } if (this.viewparams.importId) { requestUrl += `?srfimporttag=${this.viewparams.importId}`; } const data = new FormData(); data.append('file', this.selectedFile); axios .post(requestUrl, data, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (progressEvent: any) => { this.uploadedProgress = Math.floor((progressEvent.loaded / progressEvent.total) * 100); }, }) .then((res: any) => { // 忽略错误时的提示信息 const result: any = {}; if (res && res.status && res.status == 200) { if (this.enableAsyncImport) { AppNoticeService.getInstance().success( '正在导入数据,您可以在消息中心中查看数据导入的状态。', { duration: 5000, showClose: true }, ); this.$emit('close', []); } else { const { data: data } = res; const { errorinfo, success, total, errorfile } = data; result.total = total ? Number(total) : 0; result.success = success ? Number(success) : 0; result.errorfile = errorfile; if (errorinfo && Object.keys(errorinfo).length > 0) { result.error = Object.keys(errorinfo).length; result.errorInfos = []; Object.keys(errorinfo).forEach((item: any) => { result.errorInfos.push({ index: Number(item) + 1, errorinfo: errorinfo[item].errorInfo, }); }); } } } this.setUIState(0, false, true, result); }) .catch((error: any) => { // 不忽略错误时的提示信息 const errorResult: any = {}; if (error && error.status && error.status !== 200) { errorResult.errorInfos = []; errorResult.errorInfos.push({ errorinfo: error.data?.message || this.$t('app.commonwords.sysexception'), }); } this.setUIState(0, false, true, errorResult); }); } catch (error) { this.setUIState(0, false, true, { errorInfos: [{ errorinfo: error.data || this.$t('app.commonwords.sysexception') }], }); } } } </script> <style lang="less"> @import './app-data-upload.less'; </style>