阅行客电子档案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

369 lines
12 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. <template>
  2. <!--上传组件-->
  3. <el-dialog class="big-file" title="大文件上传" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :visible.sync="uploadBigVisible">
  4. <div class="setting-dialog">
  5. <div class="uploader-big">
  6. <uploader
  7. ref="uploader"
  8. :auto-start="false"
  9. :options="options"
  10. :file-status-text="statusText"
  11. @file-success="fileSuccess"
  12. @files-added="filesAdded"
  13. @file-error="onFileError"
  14. @file-removed="filesRemove"
  15. >
  16. <uploader-unsupport />
  17. <uploader-drop>
  18. <p>将文件拖到此处或点击上传</p>
  19. <uploader-btn single>
  20. <slot>
  21. <i class="iconfont icon-tianjiawenjian upload-icon" />
  22. </slot>
  23. </uploader-btn>
  24. <div class="el-upload__tip">上传限制文件大小最大10GB/</div>
  25. <!-- <uploader-btn :attrs="attrs">选择图片</uploader-btn>
  26. <uploader-btn :directory="true">选择文件夹</uploader-btn> -->
  27. </uploader-drop>
  28. <uploader-files />
  29. <!-- <ul class="file-list">
  30. <li
  31. v-for="file in fileList"
  32. :key="file.id"
  33. class="file-item"
  34. :class="`file-${file.id}`"
  35. >
  36. <uploader-file
  37. ref="files"
  38. :class="'file_' + file.id"
  39. :file="file"
  40. :list="true"
  41. />
  42. </li>
  43. </ul> -->
  44. </uploader>
  45. </div>
  46. <div slot="footer" class="dialog-footer">
  47. <el-button type="text" @click="handleCloseDialog">取消</el-button>
  48. <el-button :loading="btnLoading" type="primary" @click="handleUploadConfirm">保存</el-button>
  49. </div>
  50. </div>
  51. </el-dialog>
  52. </template>
  53. <script>
  54. import { mapGetters } from 'vuex'
  55. import axios from 'axios'
  56. import SparkMD5 from 'spark-md5'
  57. import { getToken } from '@/utils/auth'
  58. import { getCurrentTime } from '@/utils/index'
  59. // https://juejin.cn/post/7040817922540830728
  60. export default {
  61. props: {
  62. selectedCategory: {
  63. type: Object,
  64. default: function() {
  65. return {}
  66. }
  67. },
  68. arcId: {
  69. type: String,
  70. default: function() {
  71. return ''
  72. }
  73. }
  74. },
  75. data() {
  76. return {
  77. btnLoading: false,
  78. uploadBigVisible: false,
  79. skip: false,
  80. options: {
  81. target: '/api/collect/upload',
  82. // 开启服务端分片校验功能
  83. testChunks: true, // 是否分片
  84. singleFile: true, // 单文件上传
  85. uploadMethod: 'post', // 真正上传的时候使用的 HTTP 方法,默认 POST
  86. allowDuplicateUploads: false, // 上传过得文件不可以再上传
  87. parseTimeRemaining: function(timeRemaining, parsedTimeRemaining) {
  88. return parsedTimeRemaining
  89. .replace(/\syears?/, '年')
  90. .replace(/\days?/, '天')
  91. .replace(/\shours?/, '小时')
  92. .replace(/\sminutes?/, '分钟')
  93. .replace(/\sseconds?/, '秒')
  94. },
  95. // 服务器分片校验函数
  96. checkChunkUploadedByResponse: (chunk, message) => {
  97. const result = JSON.parse(message)
  98. if (result.data.skipUpload) {
  99. this.skip = true
  100. return true
  101. }
  102. return (result.data.uploaded || []).indexOf(chunk.offset + 1) >= 0
  103. },
  104. headers: {
  105. 'Authorization': getToken()
  106. }
  107. },
  108. attrs: {
  109. accept: 'image/*'
  110. },
  111. // 修改上传状态
  112. statusText: {
  113. success: '上传成功',
  114. error: '上传出错了',
  115. uploading: '上传中...',
  116. paused: '暂停中...',
  117. waiting: '等待中...',
  118. cmd5: '计算文件MD5中...'
  119. },
  120. fileList: [],
  121. nowDate: null,
  122. submitted: false
  123. }
  124. },
  125. computed: {
  126. ...mapGetters([
  127. 'baseApi'
  128. ])
  129. },
  130. methods: {
  131. fileSuccess(rootFile, file, response, chunk) {
  132. this.chunkOffset = []
  133. const result = JSON.parse(response)
  134. console.log('result', result)
  135. // 是否需要合并
  136. // if (result.data.needMerge && !this.skip) {
  137. // } else {
  138. // console.log('上传成功,不需要合并')
  139. // }
  140. this.fileList.push(file)
  141. if (result.code === 200 && this.fileList.length !== 0) {
  142. this.submitted = true
  143. } else {
  144. this.submitted = false
  145. }
  146. if (this.skip) {
  147. this.skip = false
  148. }
  149. },
  150. filesRemove(file, index) {
  151. this.fileList = []
  152. const uploaderInstance = this.$refs.uploader.uploader
  153. const temp = uploaderInstance.fileList.findIndex(e => e.uniqueIdentifier === file.uniqueIdentifier)
  154. if (temp > -1) {
  155. uploaderInstance.fileList[temp].cancel() // 这句代码是删除所选上传文件的关键
  156. }
  157. },
  158. handleUploadConfirm() {
  159. if (this.$refs.uploader.fileList.length === 0) {
  160. this.$message.error('请选择要上传的文件!')
  161. return false
  162. }
  163. this.nowDate = getCurrentTime()
  164. this.$refs.uploader.fileList.map(async(item, index) => {
  165. const json = {}
  166. const jsonArray = []
  167. const jsonString = {}
  168. if (item.file.type.substring(0, item.file.type.indexOf('/')) === 'image') {
  169. const fileBase64 = await this.getBase64(item)
  170. const imgRes = await this.getImgPx(fileBase64)
  171. item.file.px = imgRes.width + 'px*' + imgRes.height + 'px'
  172. } else {
  173. item.file.px = ''
  174. }
  175. jsonString.file_name = item.file.name
  176. jsonString.file_size = item.file.size
  177. jsonString.file_type = item.file.name.substring(item.name.lastIndexOf('.') + 1, item.file.name.length)
  178. // jsonString.file_path = res.data.data
  179. jsonString.file_path = ''
  180. jsonString.sequence = null
  181. jsonString.archive_id = this.arcId
  182. jsonString.file_dpi = item.file.px
  183. jsonString.file_thumbnail = ''
  184. jsonString.create_time = this.nowDate
  185. jsonString.id = null
  186. jsonArray.push(jsonString)
  187. json.categoryId = this.selectedCategory.id
  188. json.archivesId = this.arcId
  189. json.identifier = item.uniqueIdentifier
  190. json.filename = item.name
  191. // chunk.offset
  192. json.totalChunks = item.chunks.length - 1
  193. json.totalSize = item.size
  194. json.fileJsonString = JSON.stringify(jsonArray)
  195. if (item.completed && this.submitted) {
  196. this.btnLoading = true
  197. this.submitted = false
  198. axios.post(this.baseApi + '/api/collect/merge', json, { headers: {
  199. 'Authorization': getToken()
  200. }}).then((res) => {
  201. console.log(res)
  202. if (res.data.code === 200 && res.data.data !== '') {
  203. this.$message.success('上传成功')
  204. } else {
  205. this.$message.error('上传失败')
  206. }
  207. this.$emit('close-dialog')
  208. this.uploadBigVisible = false
  209. this.fileList = []
  210. this.btnLoading = false
  211. this.$refs.uploader.files = []
  212. this.$refs.uploader.fileList = []
  213. this.$refs.uploader.uploader.fileList = []
  214. this.$refs.uploader.uploader.files = []
  215. })
  216. } else {
  217. this.submitted = false
  218. this.$message.error('请耐心等待文件上传完成后再保存!')
  219. }
  220. })
  221. },
  222. onFileError(rootFile, file, message, chunk) {
  223. this.$message.error('上传出错:' + message)
  224. },
  225. filesAdded(file, fileList, event) {
  226. file.forEach((e) => {
  227. this.fileList.push(e)
  228. this.computeMD5(e)
  229. })
  230. },
  231. computeMD5(file) {
  232. const maxMessage = '上传文件大小不能超过 10GB!'
  233. const maxSize = 10 * 1024 * 1024 * 1024
  234. if (file && file.size > maxSize) {
  235. this.$message.warning(maxMessage)
  236. return false
  237. }
  238. const fileReader = new FileReader()
  239. const time = new Date().getTime()
  240. const blobSlice =
  241. File.prototype.slice ||
  242. File.prototype.mozSlice ||
  243. File.prototype.webkitSlice
  244. let currentChunk = 0
  245. // 文件分片大小
  246. const chunkSize = 10 * 1024 * 1024
  247. const chunks = Math.ceil(file.size / chunkSize)
  248. const spark = new SparkMD5.ArrayBuffer()
  249. // 文件状态设为"计算MD5"
  250. file.cmd5 = true
  251. file.pause()
  252. loadNext()
  253. fileReader.onload = (e) => {
  254. spark.append(e.target.result)
  255. if (currentChunk < chunks) {
  256. currentChunk++
  257. loadNext()
  258. // 实时展示MD5的计算进度
  259. console.log(
  260. `${currentChunk}分片解析完成, 开始第${
  261. currentChunk + 1
  262. } / ${chunks}分片解析`
  263. )
  264. } else {
  265. const md5 = spark.end()
  266. console.log(
  267. `MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${
  268. file.size
  269. } 用时${new Date().getTime() - time} ms`
  270. )
  271. spark.destroy() // 释放缓存
  272. file.uniqueIdentifier = md5 // 将文件md5赋值给文件唯一标识
  273. file.cmd5 = false // 取消计算md5状态
  274. file.resume() // 开始上传
  275. }
  276. }
  277. fileReader.onerror = function() {
  278. this.error(`文件${file.name}读取出错,请检查该文件`)
  279. file.cancel()
  280. }
  281. function loadNext() {
  282. const start = currentChunk * chunkSize
  283. const end = start + chunkSize >= file.size ? file.size : start + chunkSize
  284. fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end))
  285. }
  286. },
  287. // 将上传的图片转为base64
  288. getBase64(file) {
  289. const reader = new FileReader()
  290. reader.readAsDataURL(file)
  291. return new Promise((resolve) => {
  292. reader.onload = () => {
  293. resolve(reader.result)
  294. }
  295. })
  296. },
  297. // 获取图片的分辨率
  298. getImgPx(img) {
  299. const image = new Image()
  300. image.src = img
  301. return new Promise((resolve) => {
  302. image.onload = () => {
  303. const width = image.width
  304. const height = image.height
  305. resolve({ width, height })
  306. }
  307. })
  308. },
  309. handleCloseDialog(done) {
  310. this.uploadBigVisible = false
  311. this.fileList = []
  312. }
  313. }
  314. }
  315. </script>
  316. <style lang="scss" scoped>
  317. .uploader-big{
  318. width: 100%;
  319. .uploader{
  320. position: relative;
  321. display: flex;
  322. flex-direction: column;
  323. justify-content: center;
  324. width: 100%;
  325. text-align: center;
  326. margin-bottom: 8px;
  327. .uploader-drop{
  328. display: flex;
  329. flex-direction: column;
  330. justify-content: center;
  331. text-align: center;
  332. height: 180px;
  333. // padding: 20px 0;
  334. border: none;
  335. .uploader-btn{
  336. // width: 120px;
  337. margin: 20px 0 20px 0;
  338. padding: 0;
  339. border: none;
  340. i{
  341. font-size: 32px;
  342. color: #1F55EB;
  343. }
  344. &:hover{
  345. background-color: transparent;
  346. }
  347. }
  348. .el-upload__tip{
  349. font-size: 12px;
  350. color: #A6ADB6;
  351. }
  352. }
  353. }
  354. .upload-big-button{
  355. display: flex;
  356. flex-direction: row;
  357. justify-content: center;
  358. align-items: center;
  359. margin: 10px 0 5px 0;
  360. }
  361. }
  362. .uploader-file[status="success"] .uploader-file-remove {
  363. display:block
  364. }
  365. </style>