upload.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /**
  2. * 文件上传处理函数
  3. */
  4. import { computed, ref, watch } from 'vue'
  5. import { useCipConfig } from '../../../hooks/use-cip-config'
  6. import CipMessage from '../../../cip-message'
  7. import { useFormInput } from '../../../hooks/form-input'
  8. import { isString } from '@cip/utils/util'
  9. import { useFormInject } from '../../../hooks/use-form'
  10. export const fileTypeList = ['png', 'jpg', 'jpeg', 'jpe', 'gif', 'doc', 'docx', 'ppt', 'pptx', 'pdf', 'xls', 'xlsx', 'zip', '7z', 'rar']
  11. export const uploadProps = (props, context) => {
  12. const { proxyValue, securityConfig } = useFormInput(props, context)
  13. const cipForm = useFormInject()
  14. const cipConfig = useCipConfig()
  15. const useStringValue = computed(() => {
  16. return securityConfig.value.stringValue
  17. })
  18. const objectValue = computed(() => { // 返回的数据是一个对象
  19. return securityConfig.value.objectValue
  20. })
  21. const uploadFileFn = computed(() => {
  22. // BROKEN: 默认的文件上传方法由cip-config-porvide提供
  23. return securityConfig.value.uploadFn || cipConfig.fileUpload
  24. })
  25. const fileSplitKey = computed(() => {
  26. return securityConfig.value.splitKey ?? ','
  27. })
  28. const fileList = ref([])
  29. watch(() => props.modelValue, () => {
  30. if (props.modelValue) {
  31. if (!useStringValue.value) {
  32. // 兼容未配置stringValue但存储结果为string的modelValue
  33. if (isString(props.modelValue)) {
  34. fileList.value = props.modelValue.split(fileSplitKey.value).map(v => ({ url: v }))
  35. } else if (objectValue.value) {
  36. fileList.value = [props.modelValue]
  37. } else {
  38. fileList.value = props.modelValue
  39. }
  40. } else {
  41. fileList.value = props.modelValue.split(fileSplitKey.value).map(v => ({ url: v }))
  42. }
  43. } else {
  44. fileList.value = []
  45. }
  46. }, { immediate: true })
  47. // 文件上传
  48. const uploadFile = (info) => {
  49. let file = info.file
  50. const uid = info.file.uid
  51. if (securityConfig.value.getFileName) {
  52. // 重新命名文件
  53. file = new window.File([file], securityConfig.value.getFileName(file.name, props.dependOnValues, securityConfig.value))
  54. }
  55. // 保持uid
  56. file.uid = uid
  57. const fileObject = {
  58. name: file.name,
  59. uid: file.uid,
  60. percentage: 0,
  61. status: ''
  62. }
  63. const fileXhr = uploadFileFn.value({
  64. file: file,
  65. dependOnValues: props.dependOnValues,
  66. config: {
  67. // 挂载监听钩子
  68. onUploadProgress: progress => {
  69. fileUploadProgress(info, { total: progress.total, loaded: progress.loaded, percentage: Math.floor(progress.loaded / progress.total * 100) })
  70. }
  71. }
  72. })
  73. fileObject.abort = fileXhr.abort
  74. fileList.value.push(fileObject)
  75. // 限制表单提交
  76. cipForm.uploadQueue[uid] = true
  77. fileXhr.send().then((res) => {
  78. fileUploadProgress(info, { url: res.data, status: 'success' })
  79. if (props.config.autoNotify) CipMessage.success(res.message)
  80. updateValue()
  81. }).catch(e => {
  82. fileUploadProgress(info, { status: 'exception' })
  83. setTimeout(() => {
  84. fileList.value.splice(fileList.value.findIndex(v => v.uid === info.file.uid), 1)
  85. }, 500)
  86. }).finally(() => {
  87. cipForm.uploadQueue[uid] = false
  88. })
  89. }
  90. // 单例模式加载message
  91. let messageInstance = null
  92. const mainMessage = (through, message) => {
  93. if (!through) {
  94. if (messageInstance) {
  95. CipMessage.closeAll()
  96. }
  97. messageInstance = CipMessage.warning(message)
  98. return true
  99. }
  100. }
  101. // 文件数量限制
  102. const countLimit = (file) => {
  103. // 加上正在上传的数量
  104. return fileList.value.length + 1 <= (securityConfig.value.limit ?? Infinity)
  105. }
  106. // 文件类型限制
  107. const typeLimit = (file) => {
  108. if (props.config.ignoreFileType) { // 完全不限制类型
  109. return true
  110. }
  111. const fileType = props.config.fileType && securityConfig.value.fileType.length !== 0 ? securityConfig.value.fileType : fileTypeList
  112. const obj = file.name?.split('.') ?? ''
  113. return fileType.includes(obj[obj.length - 1])
  114. }
  115. // 文件大小限制
  116. const sizeLimit = (file) => {
  117. return file.size <= (securityConfig.value.size ?? Infinity) * 1024 * 1024
  118. }
  119. // 修改文件列表某文件的状态
  120. const fileUploadProgress = (info, writeObj) => {
  121. return fileList.value.map(i => {
  122. if (i.uid === info.file.uid) {
  123. // eslint-disable-next-line
  124. for (const item in writeObj) {
  125. i[item] = writeObj[item]
  126. }
  127. }
  128. return i
  129. })
  130. }
  131. // 文件列表删除
  132. const removeFile = (file, list) => {
  133. const fileIndex = fileList.value.findIndex(v => v.uid === file.uid)
  134. fileList.value.splice(fileIndex, 1)
  135. updateValue()
  136. }
  137. const updateValue = () => {
  138. let modelValue = fileList.value
  139. if (useStringValue.value) {
  140. modelValue = fileList.value?.map(v => v.url).join(fileSplitKey.value)
  141. } else if (objectValue.value) {
  142. modelValue = fileList.value[0]
  143. }
  144. proxyValue.value = modelValue
  145. }
  146. return {
  147. uploadFile,
  148. removeFile,
  149. fileList,
  150. mainMessage,
  151. countLimit,
  152. typeLimit,
  153. sizeLimit
  154. }
  155. }
  156. export const download = (file) => {
  157. file.percentage = ''
  158. const download = document.createElement('a')
  159. download.setAttribute('href', file.url + '?response-content-type=application/octet-stream')
  160. download.setAttribute('download', file.name)
  161. download.click()
  162. }