HcUpload.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <template>
  2. <el-upload
  3. ref="uploadRef"
  4. :accept="accept" :action="action" :before-remove="delUploadData" :before-upload="beforeUpload"
  5. :data="uploadData"
  6. :disabled="isCanuploadVal" :file-list="fileListData" :headers="getHeader()" :on-error="uploadError"
  7. :on-exceed="uploadExceed" :on-preview="uploadPreview" :on-progress="uploadprogress"
  8. :on-remove="uploadRemove" :on-success="uploadSuccess" class="hc-upload-border"
  9. :class="autoUpload === false ? 'hc-upload-border1' : 'hc-upload-border'"
  10. drag multiple
  11. :auto-upload="autoUpload"
  12. :on-change="handleFileChange"
  13. >
  14. <!-- 使用file插槽自定义文件列表 -->
  15. <!-- 使用draggable组件包裹文件列表 -->
  16. <draggable v-model="fileListData" @end="onDragEnd">
  17. <template #item="{ element }">
  18. <div class="file-item">
  19. <HcIcon name="file" class="file-icon" />
  20. <span class="file-name">{{ element.name }}</span>
  21. <HcIcon name="close" class="float-right cursor-pointer text-red" @click.stop="handleRemove(element)" />
  22. </div>
  23. </template>
  24. </draggable>
  25. <template #trigger>
  26. <div v-loading="uploadDisabled" :element-loading-text="loadingText" class="hc-upload-loading h-full" @click.stop="beforesubmitUpload">
  27. <HcIcon name="backup" ui="text-5xl mt-4" />
  28. <div class="el-upload__text">拖动文件到这里 或 <em>点击这里选择文件</em></div>
  29. </div>
  30. </template>
  31. <template #tip>
  32. <div class="el-upload__tip" style="font-size: 14px;">
  33. {{ acceptTip }}
  34. </div>
  35. </template>
  36. </el-upload>
  37. <div class="mt-3" style="float: right;">
  38. <el-button v-if="!autoUpload" type="primary" @click="submitUpload">
  39. 确认上传
  40. </el-button>
  41. </div>
  42. </template>
  43. <script setup>
  44. import { nextTick, onMounted, ref, watch } from 'vue'
  45. import { getHeader } from 'hc-vue3-ui'
  46. import wbsApi from '~api/data-fill/wbs'
  47. import { isFileSize } from 'js-fast-way'
  48. import { toPdfPage } from '~uti/btn-auth'
  49. import draggable from 'vuedraggable'
  50. const props = defineProps({
  51. fileList: {
  52. type: Array,
  53. default: () => ([]),
  54. },
  55. datas: {
  56. type: Object,
  57. default: () => ({}),
  58. },
  59. isCanupload:{
  60. type:Boolean,
  61. default:false,
  62. },
  63. action:{
  64. type:String,
  65. default:'#',
  66. },
  67. accept:{
  68. type:String,
  69. default:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,application/pdf,.doc,.docx,application/msword',
  70. },
  71. acceptTip:{
  72. type:String,
  73. default:'允许格式:pdf/excel/word, 文件大小 小于 60MB',
  74. },
  75. autoUpload:{
  76. type:Boolean,
  77. default:true,
  78. },
  79. typevalue:{
  80. type:[String, Number],
  81. default:'',
  82. }, //附件类型
  83. })
  84. //事件
  85. const emit = defineEmits(['change', 'close'])
  86. //变量
  87. const uploadData = ref(props.datas)
  88. const fileListData = ref(props.fileList)
  89. const action = ref(props.action)
  90. const accept = ref(props.accept)
  91. const acceptTip = ref(props.acceptTip)
  92. const uploadDisabled = ref(false)
  93. const isCanuploadVal = ref(props.isCanupload)
  94. const autoUpload = ref(props.autoUpload)
  95. const typevalue = ref(props.typevalue)
  96. //监听
  97. watch(() => [
  98. props.fileList,
  99. props.datas,
  100. props.isCanupload,
  101. props.action,
  102. props.accept,
  103. props.acceptTip,
  104. props.autoUpload,
  105. props.typevalue,
  106. ], ([fileList, datas, isCanupload, Action, Accept, Tip, auto, type]) => {
  107. uploadData.value = datas
  108. fileListData.value = fileList
  109. isCanuploadVal.value = isCanupload
  110. action.value = Action
  111. accept.value = Accept
  112. acceptTip.value = Tip
  113. autoUpload.value = auto
  114. typevalue.value = type
  115. })
  116. watch(() => [
  117. props.typevalue,
  118. props.autoUpload,
  119. ], ([ type, auto]) => {
  120. typevalue.value = type
  121. autoUpload.value = auto
  122. },
  123. { immediate: true },
  124. )
  125. // 在watch中添加对fileList的深度监听
  126. watch(() => props.fileList, (newVal) => {
  127. console.log(newVal, 'newVal')
  128. fileListData.value = [...newVal] // 使用新数组保证响应性
  129. }, { deep: true, immediate: true })
  130. //渲染完成
  131. onMounted(() => {
  132. beforeFileNum.value = 0
  133. finishFileNum.value = 0
  134. errorFileNum.value = 0
  135. })
  136. //上传前
  137. const beforeFileNum = ref(0)
  138. const beforeUpload = async (file) => {
  139. if (isFileSize(file?.size, 60)) {
  140. beforeFileNum.value++
  141. // 获取当前文件的索引
  142. const fileIndex = fileListData.value.findIndex(f => f.raw === file.raw)
  143. // 设置uploadData中的sort参数
  144. uploadData.value.sort = fileIndex + 1
  145. return true
  146. } else {
  147. window?.$message?.warning('文件大小, 不能过60M!')
  148. return false
  149. }
  150. }
  151. //超出限制时
  152. const uploadExceed = () => {
  153. window?.$message?.warning('请上传 jpg/png/pdf/excel/word 的文件,文件大小 不超过60M')
  154. }
  155. const q = 1 // 假设q是固定偏移量,可以根据需要调整
  156. // 新增的处理方法
  157. // 文件变化处理
  158. const handleFileChange = (file, fileList) => {
  159. console.log(fileList, 'fileList')
  160. fileListData.value = fileList.map((item, index) => ({
  161. ...item,
  162. sort: index + q, // 为每个文件添加sort字段
  163. }))
  164. }
  165. // 新增的拖拽结束事件处理函数
  166. const onDragEnd = (event) => {
  167. console.log('拖拽结束', event)
  168. // 可以在这里处理拖拽结束后的逻辑,比如更新文件顺序等
  169. }
  170. // 手动删除文件
  171. const handleRemove = (file) => {
  172. const index = fileListData.value.findIndex(f => f.uid === file.uid)
  173. if (index !== -1) {
  174. fileListData.value.splice(index, 1)
  175. }
  176. }
  177. //上传中
  178. const loadingText = ref('上传中...')
  179. const uploadprogress = () => {
  180. loadingText.value = '上传中...'
  181. uploadDisabled.value = true
  182. }
  183. //上传完成
  184. const finishFileNum = ref(0)
  185. const uploadSuccess = () => {
  186. finishFileNum.value++
  187. if (beforeFileNum.value === finishFileNum.value) {
  188. uploadDisabled.value = false
  189. emit('change', { type: 'success' })
  190. }
  191. }
  192. //上传失败
  193. const errorFileNum = ref(0)
  194. const uploadError = () => {
  195. errorFileNum.value++
  196. window?.$message?.error('上传失败')
  197. const num = finishFileNum.value + errorFileNum.value
  198. if (beforeFileNum.value === num) {
  199. uploadDisabled.value = false
  200. emit('change', { type: 'success' })
  201. }
  202. }
  203. //预览
  204. const uploadPreview = ({ url }) => {
  205. emit('close')
  206. toPdfPage(url)
  207. /*if (url) {
  208. window.open(url, '_blank')
  209. }*/
  210. }
  211. const uploadRef = ref(null)
  212. //删除文件
  213. const delUploadData = async (res) => {
  214. const { id, status } = res
  215. console.log(res, 'res')
  216. if (accept.value === 'application/pdf') {
  217. if (!id || status === 'uploading') {
  218. uploadRef.value.abort()
  219. uploadDisabled.value = false
  220. return true
  221. } else {
  222. loadingText.value = '删除中...'
  223. uploadDisabled.value = true
  224. const { error, code, msg } = await wbsApi.delTabById({
  225. ids: id,
  226. })
  227. uploadDisabled.value = false
  228. if (!error && code === 200) {
  229. window?.$message?.success('删除成功')
  230. return true
  231. } else {
  232. window?.$message?.error(msg || '操作失败')
  233. return false
  234. }
  235. }
  236. } else {
  237. if (!id || status === 'uploading') {
  238. uploadRef.value.abort()
  239. uploadDisabled.value = false
  240. return true
  241. } else {
  242. loadingText.value = '删除中...'
  243. uploadDisabled.value = true
  244. const { error, code, msg } = await wbsApi.removeBussFile({
  245. ids: id,
  246. })
  247. uploadDisabled.value = false
  248. if (!error && code === 200) {
  249. window?.$message?.success('删除成功')
  250. return true
  251. } else {
  252. window?.$message?.error(msg || '操作失败')
  253. return false
  254. }
  255. }
  256. }
  257. }
  258. const uploadRemove = () => {
  259. if (fileListData.value.length <= 0) {
  260. emit('change', { type: 'del' })
  261. }
  262. }
  263. const beforesubmitUpload = () => {
  264. if (!typevalue.value && !autoUpload.value) {
  265. window.$message.warning('请先选择附件类型')
  266. return
  267. } else {
  268. const uploadInput = uploadRef.value.$el.querySelector('input[type=file]')
  269. if (uploadInput) {
  270. uploadInput.click()
  271. }
  272. }
  273. }
  274. const submitUpload = async ()=>{
  275. if (!typevalue.value && !autoUpload.value) {
  276. window.$message.warning('请先选择附件类型')
  277. return
  278. }
  279. // 准备文件数据(确保有sort参数)
  280. const filesWithSort = fileListData.value.map((file, index) => ({
  281. file: file.raw || file, // 源文件
  282. sort: index + q, // 排序值
  283. }))
  284. // 创建 FormData 对象
  285. const formData = new FormData()
  286. // 2. 单独添加每个文件(保持文件二进制数据)
  287. filesWithSort.forEach((item, index) => {
  288. formData.append(`file_${index}`, item.file, item.file.name)
  289. })
  290. // 添加其他必要的上传参数
  291. formData.append('classify', uploadData.value.classify)
  292. formData.append('nodeId', uploadData.value.nodeId)
  293. formData.append('type', uploadData.value.type)
  294. formData.append('contractId', uploadData.value.contractId)
  295. const { error, code, msg } = await wbsApi.addBussFileNode(formData)
  296. uploadDisabled.value = false
  297. if (!error && code === 200) {
  298. window?.$message?.success('删除成功')
  299. return true
  300. } else {
  301. window?.$message?.error(msg || '操作失败')
  302. return false
  303. }
  304. // uploadRef.value.submit()
  305. }
  306. </script>
  307. <style lang="scss">
  308. .hc-upload-border1 .el-upload-dragger{
  309. padding: 0px;
  310. }
  311. .hc-upload-border1 .el-upload-dragger .el-upload__text{
  312. padding: 40px;
  313. }
  314. </style>