322 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			322 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | ||
|   <section class="ai-import">
 | ||
|     <a v-if="$slots.default" class="custom-clicker" @click="dialog = true">
 | ||
|       <slot/>
 | ||
|     </a>
 | ||
|     <el-button v-else size="small" @click="dialog=true" icon="iconfont iconImport">导入</el-button>
 | ||
|     <ai-dialog
 | ||
|       :title="dialogTitle"
 | ||
|       :visible.sync="dialog"
 | ||
|       :destroy-on-close="true"
 | ||
|       width="800px"
 | ||
|       customFooter
 | ||
|       :close-on-click-modal="false"
 | ||
|       @closed="onClose">
 | ||
|       <el-form size="small" ref="importForm" label-width="0" class="import-form" :model="fileForm"
 | ||
|                :rules="fileRules">
 | ||
|         <el-form-item class="ai-import__tips">
 | ||
|           <h2>导入说明:</h2>
 | ||
|           <p class="ai-import__content">
 | ||
|             1、您正在进行【{{ name }}】批量导入操作,请先
 | ||
|             <span @click="downloadFile" title="下载导入模板" class="ai-link" v-text="'【点击下载导入模板】'"/>
 | ||
|             并规范填写;</p>
 | ||
|           <p class="ai-import__content">2、填写完成后,上传您编辑完成的模板文件:</p>
 | ||
|           <div class="ai-import__text">
 | ||
|             <template v-if="$slots.tips">
 | ||
|               <div>请注意:</div>
 | ||
|               <slot name="tips"/>
 | ||
|             </template>
 | ||
|             <el-row type="flex" v-else-if="!!dict">
 | ||
|               <div v-text="'请注意:'"/>
 | ||
|               <span v-text="dict.getLabel('importTips',type)"/>
 | ||
|             </el-row>
 | ||
|           </div>
 | ||
|         </el-form-item>
 | ||
|         <el-form-item prop="file" style="width: 100%">
 | ||
|           <ai-uploader isImport :instance="instance" v-model="fileForm.file" fileType="file" :limit="1"
 | ||
|                        acceptType=".xls,.xlsx" @change="onChange" :clearable="false">
 | ||
|             <template #trigger>
 | ||
|               <el-button icon="iconfont iconfangda">选择文件</el-button>
 | ||
|             </template>
 | ||
|             <template #tips>最多上传1个文件,单个文件最大10MB,仅支持Excel格式</template>
 | ||
|           </ai-uploader>
 | ||
|         </el-form-item>
 | ||
|       </el-form>
 | ||
|       <div slot="footer" style="text-align: center;">
 | ||
|         <el-button @click="dialog=false">取消</el-button>
 | ||
|         <el-button type="primary" @click="onClick">立即导入</el-button>
 | ||
|       </div>
 | ||
|     </ai-dialog>
 | ||
|     <div class="ai-import__loading" v-if="isHasLoadingSlot && isLoading">
 | ||
|       <slot name="loading"/>
 | ||
|     </div>
 | ||
|   </section>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| export default {
 | ||
|   name: 'AiImport',
 | ||
|   props: {
 | ||
|     title: String,
 | ||
|     name: {
 | ||
|       type: String,
 | ||
|       required: true
 | ||
|     },
 | ||
|     instance: {
 | ||
|       type: Function
 | ||
|     },
 | ||
|     importUrl: {
 | ||
|       type: String,
 | ||
|     },
 | ||
|     importParams: {
 | ||
|       type: Object
 | ||
|     },
 | ||
|     suffixName: {
 | ||
|       type: String,
 | ||
|       default: 'xls'
 | ||
|     },
 | ||
|     tplParams: Object,
 | ||
|     url: {
 | ||
|       type: String,
 | ||
|     },
 | ||
|     timeout: {
 | ||
|       type: Number,
 | ||
|       default: 10 * 60 * 1000
 | ||
|     },
 | ||
|     customError: {
 | ||
|       type: Boolean,
 | ||
|       default: false
 | ||
|     },
 | ||
|     type: String,
 | ||
|     dict: Object
 | ||
|   },
 | ||
|   computed: {
 | ||
|     isHasLoadingSlot() {
 | ||
|       return this.$slots.loading
 | ||
|     },
 | ||
|     actions() {
 | ||
|       return {
 | ||
|         url: this.importUrl || `/app/${this.type}/import`,//导入接口
 | ||
|         tpl: this.url || `/app/${this.type}/downloadTemplate`,//下载模板接口
 | ||
|       }
 | ||
|     },
 | ||
|     dialogTitle() {
 | ||
|       return this.title || `${this.name}数据导入`
 | ||
|     }
 | ||
|   },
 | ||
|   data() {
 | ||
|     return {
 | ||
|       dialog: false,
 | ||
|       fileForm: {
 | ||
|         file: []
 | ||
|       },
 | ||
|       loading: null,
 | ||
|       isLoading: false,
 | ||
|       fileRules: {
 | ||
|         file: [{required: true, message: '请上传相关文件', trigger: 'change'}]
 | ||
|       }
 | ||
|     }
 | ||
|   },
 | ||
|   methods: {
 | ||
|     onChange(e) {
 | ||
|       if (e.length) {
 | ||
|         this.$refs.importForm.clearValidate()
 | ||
|       } else {
 | ||
|         this.$refs.importForm.validate()
 | ||
|       }
 | ||
|     },
 | ||
|     onClick() {
 | ||
|       this.$refs.importForm.validate(v => {
 | ||
|         if (v) {
 | ||
|           const data = new FormData()
 | ||
|           data.append('file', this.fileForm.file[0].raw)
 | ||
|           if (!this.isHasLoadingSlot) {
 | ||
|             this.loading = this.$loading({
 | ||
|               lock: true,
 | ||
|               text: '导入中',
 | ||
|               background: 'rgba(0, 0, 0, 0.5)'
 | ||
|             })
 | ||
|           } else {
 | ||
|             this.isLoading = true
 | ||
|           }
 | ||
|           this.instance.post(this.actions.url, data, {
 | ||
|             params: this.importParams,
 | ||
|             timeout: this.timeout
 | ||
|           }).then(res => {
 | ||
|             if (!this.isHasLoadingSlot) {
 | ||
|               this.loading?.close()
 | ||
|             } else {
 | ||
|               this.isLoading = false
 | ||
|             }
 | ||
|             if (res?.data?.importStatus == 1) {
 | ||
|               this.dialog = false
 | ||
|               const h = this.$createElement
 | ||
|               this.$emit('onSuccess', res)
 | ||
|               this.$emit('success', res)
 | ||
|               if (this.customError) {
 | ||
|                 this.$emit('error')
 | ||
|               } else this.$confirm('导入失败数据具体原因请', {
 | ||
|                 type: 'success',
 | ||
|                 title: '数据导入完成',
 | ||
|                 closeOnClickModal: false,
 | ||
|                 customClass: 'message-wrapper',
 | ||
|                 showConfirmButton: false,
 | ||
|                 cancelButtonText: '关闭',
 | ||
|                 message: h('div', {
 | ||
|                   class: 'importResult'
 | ||
|                 }, [
 | ||
|                   h('span', null, '成功新增'),
 | ||
|                   h('a', {
 | ||
|                     style: 'color: #2EA222;'
 | ||
|                   }, `${res.data.addCount}`),
 | ||
|                   h('span', null, '条数据,更新'),
 | ||
|                   h('a', {
 | ||
|                     style: 'color: #26f;'
 | ||
|                   }, `${res.data.updateCount}`),
 | ||
|                   h('span', null, '条数据,导入失败'),
 | ||
|                   h('a', {
 | ||
|                     style: 'color: #f46;'
 | ||
|                   }, `${res.data.failCount}`),
 | ||
|                   h('span', null, '条数据。'),
 | ||
|                   h('div', {class: 'gap'}),
 | ||
|                   h('div', {style: `display:${res.data.errorFileURL ? 'block' : 'none'}`}, [
 | ||
|                     h('span', null, '点此'),
 | ||
|                     h('a', {
 | ||
|                       class: 'tips-link',
 | ||
|                       attrs: {
 | ||
|                         href: res.data.errorFileURL
 | ||
|                       }
 | ||
|                     }, '下载异常数据')
 | ||
|                   ])
 | ||
|                 ])
 | ||
|               })
 | ||
|             } else if (res?.data?.importStatus == 0) {
 | ||
|               this.$message.error(res?.data?.errorMsg)
 | ||
|             }
 | ||
|           }).catch(() => {
 | ||
|             if (!this.isHasLoadingSlot) {
 | ||
|               this.loading?.close()
 | ||
|             } else {
 | ||
|               this.isLoading = false
 | ||
|             }
 | ||
|           })
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
|     onClose() {
 | ||
|       this.loading?.close()
 | ||
|       this.fileForm.file = []
 | ||
|     },
 | ||
|     downloadFile() {
 | ||
|       this.instance.post(this.actions.tpl, null, {
 | ||
|         responseType: 'blob',
 | ||
|         params: this.tplParams
 | ||
|       }).then((res) => {
 | ||
|         const link = document.createElement('a')
 | ||
|         let blob = new Blob([res], {type: 'application/vnd.ms-excel'})
 | ||
|         link.style.display = 'none'
 | ||
|         link.href = URL.createObjectURL(blob)
 | ||
|         link.setAttribute('download', this.name + '模板.' + this.suffixName)
 | ||
|         document.body.appendChild(link)
 | ||
|         link.click()
 | ||
|         document.body.removeChild(link)
 | ||
|       })
 | ||
|     },
 | ||
|     hide() {
 | ||
|       this.dialog = false
 | ||
|     }
 | ||
|   },
 | ||
|   created() {
 | ||
|     this.dict?.load("importTips")
 | ||
|   }
 | ||
| }
 | ||
| </script>
 | ||
| <style lang="scss" scoped>
 | ||
| .ai-import {
 | ||
|   .empty-input {
 | ||
|     opacity: 0;
 | ||
|     position: absolute;
 | ||
|     z-index: -1;
 | ||
|     visibility: hidden;
 | ||
|   }
 | ||
| 
 | ||
| 
 | ||
|   .custom-clicker {
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|   }
 | ||
| 
 | ||
|   .ai-import__loading {
 | ||
|     position: fixed;
 | ||
|     left: 0;
 | ||
|     top: 0;
 | ||
|     width: 100%;
 | ||
|     height: 100%;
 | ||
|     background: rgba(0, 0, 0, 0.6);
 | ||
|   }
 | ||
| 
 | ||
|   :deep( .el-message-box ){
 | ||
|     width: 720px !important;
 | ||
|   }
 | ||
| 
 | ||
|   .ai-import__content {
 | ||
|     line-height: 22px;
 | ||
|   }
 | ||
| 
 | ||
|   .ai-import__tips {
 | ||
|     line-height: 1;
 | ||
| 
 | ||
|     :deep(.el-form-item__content ){
 | ||
|       line-height: 1;
 | ||
|     }
 | ||
| 
 | ||
|     h2 {
 | ||
|       margin-top: 4px;
 | ||
|       color: #333333;
 | ||
|       font-size: 16px;
 | ||
|       font-weight: 700;
 | ||
|     }
 | ||
| 
 | ||
|     p {
 | ||
|       margin-top: 8px;
 | ||
|       color: #424242;
 | ||
|       font-size: 14px;
 | ||
|     }
 | ||
| 
 | ||
|     .ai-link {
 | ||
|       cursor: pointer;
 | ||
|       color: $primaryColor;
 | ||
|     }
 | ||
| 
 | ||
|     .ai-import__text {
 | ||
|       font-size: 12px;
 | ||
|       margin-top: 8px;
 | ||
|       line-height: 16px;
 | ||
|       color: #999999;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 | ||
| <style lang="scss">
 | ||
| .message-wrapper {
 | ||
|   width: 560px !important;
 | ||
| 
 | ||
|   .importResult {
 | ||
|     color: #222222;
 | ||
|     font-size: 16px;
 | ||
|     line-height: 24px;
 | ||
|     font-weight: bold;
 | ||
| 
 | ||
|     .gap {
 | ||
|       width: 100%;
 | ||
|       height: 8px;
 | ||
|     }
 | ||
| 
 | ||
|     .tips-link {
 | ||
|       color: $primaryColor;
 | ||
|       text-decoration: unset;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |