11<template >
2- <div v-loading = " loading " class =" w-full" >
2+ <div class =" w-full" >
33 <el-upload
44 ref =" UploadRef"
55 :webkitdirectory =" false"
3737 </div >
3838 </div >
3939 </el-upload >
40- <el-row :gutter =" 8" v-if =" modelValue?.length" class =" mt-16" >
41- <template v-for =" (item , index ) in modelValue " :key =" index " >
40+ <div v-if =" fileArray?.length" class =" flex-between w-full mt-16" >
41+ <span >
42+ {{
43+ $t('dynamicsForm.UploadInput.uploadStatus', {
44+ success: successCount,
45+ total: fileArray.length,
46+ })
47+ }}
48+ </span >
49+ <span v-if =" uploadingCount" class =" flex align-center" >
50+ <el-icon class =" is-loading color-primary" size =" 18" ><Loading /></el-icon >
51+ <span class =" ml-4" >{{ $t('dynamicsForm.UploadInput.uploading') }}</span >
52+ </span >
53+ <span v-else-if =" errorCount" class =" flex align-center" >
54+ <el-icon class =" color-danger ml-4" size =" 18" ><WarningFilled /></el-icon >
55+ <span class =" ml-4" >
56+ {{ $t('dynamicsForm.UploadInput.failedStatus', { count: errorCount }) }}
57+ </span >
58+ <el-button v-if =" retryList.length" text @click =" retryAll" >
59+ <AppIcon iconName="app-refresh"></AppIcon >
60+ {{ $t('dynamicsForm.UploadInput.reUpload') }}
61+ </el-button >
62+ </span >
63+ </div >
64+ <el-row :gutter =" 8" v-if =" fileArray?.length" class =" mt-8" >
65+ <template v-for =" (item , index ) in fileArray " :key =" index " >
4266 <el-col :span =" 12" class =" mb-8" >
43- <el-card shadow =" never" style =" --el-card-padding : 8px 12px ; line-height : normal " >
67+ <el-card
68+ shadow =" never"
69+ style ="
70+ --el-card-padding : 8px 12px ;
71+ line-height : normal ;
72+ position : relative ;
73+ overflow : hidden ;
74+ "
75+ :class =" item.status === 'error' ? 'border-danger' : ''"
76+ >
4477 <div class =" flex-between" >
4578 <div class =" flex" >
4679 <img :src =" getImgUrl(item && item?.name)" alt =" " width =" 40" />
4780 <div class =" ml-8" >
4881 <p class =" ellipsis-1" :title =" item && item?.name" >{{ item && item?.name }}</p >
49- <el-text type =" info" size =" small" >{{
50- filesize(item && item?.size) || '0K'
51- }}</el-text >
82+ <el-text type =" info" size =" small" >
83+ <template v-if =" item .status === ' uploading' " >
84+ {{ filesize((item.size * item.percentage) / 100) }} /
85+ {{ filesize(item.size) || '0K' }}
86+ </template >
87+ <template v-else >{{ filesize(item && item?.size) || '0K' }}</template >
88+ </el-text >
89+ <el-text class =" ml-8" v-if =" item.status === 'error'" type =" danger" size =" small" >
90+ {{ item.errMsg }}
91+ </el-text >
5292 </div >
5393 </div >
54- <el-button text @click =" deleteFile(index)" >
55- <AppIcon iconName="app-delete"></AppIcon >
56- </el-button >
94+ <div class =" flex align-center" >
95+ <el-button v-if =" item.canRetry" text @click =" uploadFile(item)" >
96+ <AppIcon iconName="app-refresh"></AppIcon >
97+ </el-button >
98+ <el-button text @click =" deleteFile(index, item)" >
99+ <AppIcon iconName="app-delete"></AppIcon >
100+ </el-button >
101+ </div >
57102 </div >
103+ <el-progress
104+ v-if =" item.status === 'uploading'"
105+ class =" card-progress"
106+ :percentage =" item.percentage"
107+ :stroke-width =" 4"
108+ :show-text =" false"
109+ />
58110 </el-card >
59111 </el-col >
60112 </template >
61113 </el-row >
62114 </div >
63115</template >
64116<script setup lang="ts">
65- import { computed , useAttrs , nextTick , inject , ref } from ' vue'
117+ import { computed , useAttrs , nextTick , inject , ref , reactive } from ' vue'
66118import type { FormField } from ' @/components/dynamics-form/type'
67119import { MsgError } from ' @/utils/message'
68120import type { UploadFiles } from ' element-plus'
69121import { filesize , getImgUrl , fileType } from ' @/utils/common'
122+ import applicationApi from ' @/api/application/application'
70123import { t } from ' @/locales'
71124const upload = inject (' upload' ) as any
72125const attrs = useAttrs () as any
@@ -81,48 +134,111 @@ const onExceed = () => {
81134 )
82135}
83136const emit = defineEmits ([' update:modelValue' ])
137+
84138const fileArray = ref <any >([])
85- const loading = ref <boolean >(false )
86- const UploadRef = ref ()
139+ const loading = ref (false )
140+ // 上传成功数量
141+ const successCount = computed (
142+ () => fileArray .value .filter ((i : any ) => i .status !== ' uploading' ).length ,
143+ )
144+ // 上传失败数量
145+ const errorCount = computed (() => fileArray .value .filter ((i : any ) => i .status === ' error' ).length )
146+ // 上传中数量
147+ const uploadingCount = computed (
148+ () => fileArray .value .filter ((i : any ) => i .status === ' uploading' ).length ,
149+ )
150+ // 可重新上传的失败项(网络错误等)
151+ const retryList = computed (() =>
152+ fileArray .value .filter ((i : any ) => i .status === ' error' && i .canRetry ),
153+ )
154+ // 重新上传所有可重试的失败文件
155+ const retryAll = () => {
156+ retryList .value .forEach ((i : any ) => uploadFile (i ))
157+ }
87158// 上传on-change事件
88159const fileHandleChange = (file : any , fileList : UploadFiles ) => {
89- // 按文件唯一标识精确定位并移除当前文件
90- // 注意:不能使用 splice(-1, 1) 盲删末尾元素,文件夹上传时会误删正常文件而放走超限文件
91- const removeCurrentFile = () => {
92- const index = fileList .findIndex ((item : any ) => item .uid === file .uid )
93- if (index !== - 1 ) {
94- fileList .splice (index , 1 )
95- }
96- }
160+ const item = reactive ({
161+ name: file .name ,
162+ size: file .size ,
163+ file_id: ' ' ,
164+ percentage: 0 ,
165+ status: ' uploading' as ' uploading' | ' success' | ' error' ,
166+ errMsg: ' ' ,
167+ canRetry: false ,
168+ raw: file .raw ,
169+ abort: null as null | (() => void ),
170+ aborted: false ,
171+ })
172+
97173 // 1、判断文件大小是否合法,文件限制不能大于100M
98174 const isLimit = file ?.size / 1024 / 1024 < file_size_limit .value
99175 if (! isLimit ) {
100- MsgError (t (' views.document.tip.fileLimitSizeTip1' ) + file_size_limit .value + ' MB' )
101- removeCurrentFile () // 移除当前超出大小的文件
176+ item .status = ' error'
177+ item .errMsg = t (' dynamicsForm.UploadInput.errorTip.sizeError' )
178+ // MsgError(t('views.document.tip.fileLimitSizeTip1') + file_size_limit.value + 'MB')
179+ // fileList.splice(-1, 1) //移除当前超出大小的文件
180+ fileArray .value ?.push (item )
102181 return false
103182 }
104183 if (! file_type_list .value .includes (fileType (file .name ).toLocaleUpperCase ())) {
105184 if (file ?.name !== ' .DS_Store' ) {
106185 MsgError (t (' views.document.upload.errorMessage2' ))
107186 }
108- removeCurrentFile ()
109187 return false
110188 }
111189
112190 if (file ?.size === 0 ) {
113191 MsgError (t (' views.document.upload.errorMessage3' ))
114- removeCurrentFile ()
115192 return false
116193 }
117- upload (file .raw , loading ).then ((ok : any ) => {
118- const split_path = ok .data .split (' /' )
119- const file_id = split_path [split_path .length - 1 ]
120- fileArray .value ?.push ({ name: file .name , file_id , size: file .size })
121- emit (' update:modelValue' , fileArray .value )
122- })
194+
195+ fileArray .value ?.push (item )
196+ uploadFile (item )
123197}
124- function deleteFile(index : number ) {
125- props .modelValue .splice (index , 1 )
198+ // 执行上传
199+ const uploadFile = (item : any ) => {
200+ item .status = ' uploading'
201+ item .percentage = 0
202+ item .errMsg = ' '
203+ item .canRetry = false
204+ item .aborted = false
205+ const res: any = upload (
206+ item .raw ,
207+ (percent : number ) => {
208+ item .percentage = percent
209+ },
210+ loading ,
211+ )
212+ // provider 返回 { request, abort } 时保存中断方法,删除时可中断上传
213+ item .abort = typeof res ?.abort === ' function' ? res .abort : null
214+ const request: Promise <any > = res ?.then ? res : res ?.request
215+ request
216+ .then ((ok : any ) => {
217+ const split_path = ok .data .split (' /' )
218+ item .file_id = split_path [split_path .length - 1 ]
219+ item .percentage = 100
220+ item .status = ' success'
221+ emit (' update:modelValue' , fileArray .value )
222+ })
223+ .catch (() => {
224+ // 主动中断(删除)导致的失败不再标记错误
225+ if (item .aborted ) return
226+ item .status = ' error'
227+ item .errMsg = t (' dynamicsForm.UploadInput.errorTip.networkError' )
228+ item .canRetry = true
229+ })
230+ }
231+ function deleteFile(index : any , item ? : any ) {
232+ // 上传过程中删除则中断上传请求
233+ if (item ?.status === ' uploading' && typeof item .abort === ' function' ) {
234+ item .aborted = true
235+ item .abort ()
236+ }
237+ fileArray .value .splice (index , 1 )
238+ // if (item?.file_id) {
239+ // applicationApi.deleteFile(item.file_id)
240+ // }
241+ emit (' update:modelValue' , fileArray .value )
126242}
127243
128244const handlePreview = (bool : boolean ) => {
@@ -150,5 +266,4 @@ const file_count_limit = computed(() => {
150266 return attrs .file_count_limit || 100
151267})
152268 </script >
153- <style lang="scss" scoped>
154- </style >
269+ <style lang="scss" scoped></style >
0 commit comments