index.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <template>
  2. <div :id="id" :ref="id" :action="uploadUrl" class="dropzone">
  3. <input type="file" name="file">
  4. </div>
  5. </template>
  6. <script>
  7. import Dropzone from 'dropzone'
  8. import 'dropzone/dist/dropzone.css'
  9. // import { getToken } from 'api/qiniu';
  10. Dropzone.autoDiscover = false
  11. export default {
  12. props: {
  13. id: {
  14. type: String,
  15. required: true
  16. },
  17. url: {
  18. type: String,
  19. required: false,
  20. default: ''
  21. },
  22. clickable: {
  23. type: Boolean,
  24. default: true
  25. },
  26. defaultMsg: {
  27. type: String,
  28. default: '上传图片'
  29. },
  30. acceptedFiles: {
  31. type: String,
  32. default: 'image/*'
  33. },
  34. thumbnailHeight: {
  35. type: Number,
  36. default: 200
  37. },
  38. thumbnailWidth: {
  39. type: Number,
  40. default: 200
  41. },
  42. showRemoveLink: {
  43. type: Boolean,
  44. default: true
  45. },
  46. maxFilesize: {
  47. type: Number,
  48. default: 100
  49. },
  50. maxFiles: {
  51. type: Number,
  52. default: 100
  53. },
  54. autoProcessQueue: {
  55. type: Boolean,
  56. default: true
  57. },
  58. useCustomDropzoneOptions: {
  59. type: Boolean,
  60. default: false
  61. },
  62. defaultImg: {
  63. default: '',
  64. type: [String, Array]
  65. },
  66. couldPaste: {
  67. type: Boolean,
  68. default: false
  69. }
  70. },
  71. data() {
  72. return {
  73. dropzone: '',
  74. uploadUrl: '',
  75. initOnce: true
  76. }
  77. },
  78. watch: {
  79. defaultImg(val) {
  80. if (val.length === 0) {
  81. this.initOnce = false
  82. return
  83. }
  84. if (!this.initOnce) return
  85. this.initImages(val)
  86. this.initOnce = false
  87. }
  88. },
  89. created() {
  90. this.uploadUrl = this.url ? this.url : `${process.env.VUE_APP_BASE_API}/api/files/upload`
  91. },
  92. mounted() {
  93. const element = document.getElementById(this.id)
  94. const vm = this
  95. this.dropzone = new Dropzone(element, {
  96. headers: { Authorization: this.$store.getters.token },
  97. clickable: this.clickable,
  98. thumbnailWidth: this.thumbnailWidth,
  99. thumbnailHeight: this.thumbnailHeight,
  100. maxFiles: this.maxFiles,
  101. maxFilesize: this.maxFilesize,
  102. dictRemoveFile: 'Remove',
  103. addRemoveLinks: this.showRemoveLink,
  104. acceptedFiles: this.acceptedFiles,
  105. autoProcessQueue: this.autoProcessQueue,
  106. dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload',
  107. dictMaxFilesExceeded: '只能一个图',
  108. previewTemplate: '<div class="dz-preview dz-file-preview"> <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div> <div class="dz-details"> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-success-mark"> <i class="material-icons">done</i> </div> <div class="dz-error-mark"><i class="material-icons">error</i></div></div>',
  109. init() {
  110. const val = vm.defaultImg
  111. if (!val) return
  112. if (Array.isArray(val)) {
  113. if (val.length === 0) return
  114. val.map((v, i) => {
  115. const mockFile = { name: 'name' + i, size: 12345, url: v }
  116. this.options.addedfile.call(this, mockFile)
  117. this.options.thumbnail.call(this, mockFile, v)
  118. mockFile.previewElement.classList.add('dz-success')
  119. mockFile.previewElement.classList.add('dz-complete')
  120. vm.initOnce = false
  121. return true
  122. })
  123. } else {
  124. const mockFile = { name: 'name', size: 12345, url: val }
  125. this.options.addedfile.call(this, mockFile)
  126. this.options.thumbnail.call(this, mockFile, val)
  127. mockFile.previewElement.classList.add('dz-success')
  128. mockFile.previewElement.classList.add('dz-complete')
  129. vm.initOnce = false
  130. }
  131. },
  132. accept: (file, done) => {
  133. /* 七牛*/
  134. // const token = this.$store.getters.token;
  135. // getToken(token).then(response => {
  136. // file.token = response.data.qiniu_token;
  137. // file.key = response.data.qiniu_key;
  138. // file.url = response.data.qiniu_url;
  139. // done();
  140. // })
  141. done()
  142. },
  143. sending: (file, xhr, formData) => {
  144. // formData.append('token', file.token);
  145. // formData.append('key', file.key);
  146. vm.initOnce = false
  147. }
  148. })
  149. if (this.couldPaste) {
  150. document.addEventListener('paste', this.pasteImg)
  151. }
  152. this.dropzone.on('success', (file, response) => {
  153. file.fileUrl = response.url
  154. file.dropzoneId = vm.dropzone.element.id
  155. vm.$emit('dropzone-success', file, vm.dropzone.element)
  156. })
  157. this.dropzone.on('addedfile', file => {
  158. vm.$emit('dropzone-fileAdded', file)
  159. })
  160. this.dropzone.on('removedfile', file => {
  161. vm.$emit('dropzone-removedFile', file)
  162. })
  163. this.dropzone.on('error', (file, error, xhr) => {
  164. vm.$emit('dropzone-error', file, error, xhr)
  165. })
  166. this.dropzone.on('successmultiple', (file, error, xhr) => {
  167. vm.$emit('dropzone-successmultiple', file, error, xhr)
  168. })
  169. },
  170. destroyed() {
  171. document.removeEventListener('paste', this.pasteImg)
  172. this.dropzone.destroy()
  173. },
  174. methods: {
  175. removeAllFiles() {
  176. this.dropzone.removeAllFiles(true)
  177. },
  178. processQueue() {
  179. this.dropzone.processQueue()
  180. },
  181. pasteImg(event) {
  182. const items = (event.clipboardData || event.originalEvent.clipboardData).items
  183. if (items[0].kind === 'file') {
  184. this.dropzone.addFile(items[0].getAsFile())
  185. }
  186. },
  187. initImages(val) {
  188. if (!val) return
  189. if (Array.isArray(val)) {
  190. val.map((v, i) => {
  191. // const fileUrl = `${process.env.VUE_APP_BASE_API}/api/files/view-file?path=${v}`
  192. // const mockFile = { url: fileUrl }
  193. const mockFile = { url: v }
  194. this.dropzone.options.addedfile.call(this.dropzone, mockFile)
  195. this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v)
  196. mockFile.previewElement.classList.add('dz-success')
  197. mockFile.previewElement.classList.add('dz-complete')
  198. return true
  199. })
  200. } else {
  201. // const fileUrl = `${process.env.VUE_APP_BASE_API}/api/files/view-file?path=${val}`
  202. // const mockFile = { url: fileUrl }
  203. const mockFile = { url: val }
  204. this.dropzone.options.addedfile.call(this.dropzone, mockFile)
  205. this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val)
  206. mockFile.previewElement.classList.add('dz-success')
  207. mockFile.previewElement.classList.add('dz-complete')
  208. }
  209. }
  210. }
  211. }
  212. </script>
  213. <style scoped>
  214. .dropzone {
  215. border: 2px solid #E5E5E5;
  216. font-family: 'Roboto', sans-serif;
  217. color: #777;
  218. transition: background-color .2s linear;
  219. padding: 5px;
  220. }
  221. .dropzone:hover {
  222. background-color: #F6F6F6;
  223. }
  224. i {
  225. color: #CCC;
  226. }
  227. .dropzone .dz-image img {
  228. width: 100%;
  229. height: 100%;
  230. }
  231. .dropzone input[name='file'] {
  232. display: none;
  233. }
  234. .dropzone .dz-preview .dz-image {
  235. border-radius: 0px;
  236. }
  237. .dropzone .dz-preview:hover .dz-image img {
  238. transform: none;
  239. filter: none;
  240. width: 100%;
  241. height: 100%;
  242. }
  243. .dropzone .dz-preview .dz-details {
  244. bottom: 0px;
  245. top: 0px;
  246. color: white;
  247. background-color: rgba(33, 150, 243, 0.8);
  248. transition: opacity .2s linear;
  249. text-align: left;
  250. }
  251. .dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
  252. background-color: transparent;
  253. }
  254. .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
  255. border: none;
  256. }
  257. .dropzone .dz-preview .dz-details .dz-filename:hover span {
  258. background-color: transparent;
  259. border: none;
  260. }
  261. .dropzone .dz-preview .dz-remove {
  262. position: absolute;
  263. z-index: 30;
  264. color: white;
  265. margin-left: 15px;
  266. padding: 10px;
  267. top: inherit;
  268. bottom: 15px;
  269. border: 2px white solid;
  270. text-decoration: none;
  271. text-transform: uppercase;
  272. font-size: 0.8rem;
  273. font-weight: 800;
  274. letter-spacing: 1.1px;
  275. opacity: 0;
  276. }
  277. .dropzone .dz-preview:hover .dz-remove {
  278. opacity: 1;
  279. }
  280. .dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
  281. margin-left: -40px;
  282. margin-top: -50px;
  283. }
  284. .dropzone .dz-preview .dz-success-mark i, .dropzone .dz-preview .dz-error-mark i {
  285. color: white;
  286. font-size: 5rem;
  287. }
  288. </style>