Skip to content

Commit 51030de

Browse files
committed
add feature/dropzone page
1 parent 8ee0abd commit 51030de

File tree

5 files changed

+338
-6
lines changed

5 files changed

+338
-6
lines changed

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
},
2828
"dependencies": {
2929
"@tinymce/tinymce-vue": "^2.0.0",
30+
"@types/dropzone": "^5.0.6",
3031
"axios": "^0.18.0",
3132
"clipboard": "^2.0.4",
3233
"codemirror": "^5.46.0",
3334
"driver.js": "^0.9.6",
35+
"dropzone": "^5.5.1",
3436
"echarts": "^4.2.1",
3537
"element-ui": "^2.8.2",
3638
"file-saver": "^2.0.2",

src/components/Dropzone/index.vue

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

src/router/modules/components.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ const componentsRouter: RouteConfig = {
3838
// name: 'AvatarUploadDemo',
3939
// meta: { title: 'Upload' }
4040
// },
41-
// {
42-
// path: 'dropzone',
43-
// component: () => import('@/views/components-demo/dropzone'),
44-
// name: 'DropzoneDemo',
45-
// meta: { title: 'Dropzone' }
46-
// },
41+
{
42+
path: 'dropzone',
43+
component: () => import(/* webpackChunkName: "Dropzone" */ '@/views/components-demo/dropzone.vue'),
44+
name: 'DropzoneDemo',
45+
meta: { title: 'dropzone' }
46+
},
4747
// {
4848
// path: 'sticky',
4949
// component: () => import('@/views/components-demo/sticky'),
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<template>
2+
<div class="components-container">
3+
<aside>
4+
Based on
5+
<a
6+
class="link-type"
7+
href="https://github.com/rowanwins/vue-dropzone"
8+
>dropzone</a>
9+
.
10+
{{ $t('components.dropzoneTips') }}
11+
</aside>
12+
<div class="editor-container">
13+
<Dropzonec
14+
id="myVueDropzone"
15+
url="https://httpbin.org/post"
16+
@dropzone-removedFile="dropzoneR"
17+
@dropzone-success="dropzoneS"
18+
/>
19+
</div>
20+
</div>
21+
</template>
22+
<script lang="ts">
23+
import { Component, Vue } from 'vue-property-decorator'
24+
import Dropzonec from '@/components/Dropzone/index.vue'
25+
26+
@Component({
27+
components: {
28+
Dropzonec
29+
}
30+
})
31+
export default class Dropzone extends Vue {
32+
private dropzoneS(file: File) {
33+
console.log(file, 'file')
34+
this.$message({ message: 'Upload success', type: 'success' })
35+
}
36+
private dropzoneR(file: File) {
37+
console.log(file, 'file')
38+
this.$message({ message: 'Delete success', type: 'success' })
39+
}
40+
}
41+
</script>

yarn.lock

+12
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,13 @@
936936
dependencies:
937937
"@types/tern" "*"
938938

939+
"@types/dropzone@^5.0.6":
940+
version "5.0.6"
941+
resolved "http://r.cnpmjs.org/@types/dropzone/download/@types/dropzone-5.0.6.tgz#766fc512c2816403c7a97dd6d41bd9187355ff6c"
942+
integrity sha1-dm/FEsKBZAPHqX3W1BvZGHNV/2w=
943+
dependencies:
944+
"@types/jquery" "*"
945+
939946
"@types/echarts@^4.1.9":
940947
version "4.1.9"
941948
resolved "https://registry.yarnpkg.com/@types/echarts/-/echarts-4.1.9.tgz#86fdf7ca829879535cf3b1b1490e2f46a0ea0933"
@@ -4162,6 +4169,11 @@ driver.js@^0.9.6:
41624169
resolved "https://registry.yarnpkg.com/driver.js/-/driver.js-0.9.6.tgz#1a94d8c16104e05e62d3e467cae3570f999887f1"
41634170
integrity sha512-pJfX2aKQrhOFxHycXePXFPcZeL8jYwTWfboMq84R10xDS9vo/0s3CHEDyGn8xPRkcepzssrx4dKbuxAON1hm8Q==
41644171

4172+
dropzone@^5.5.1:
4173+
version "5.5.1"
4174+
resolved "http://r.cnpmjs.org/dropzone/download/dropzone-5.5.1.tgz#06e2f513e61d6aa363d4b556f18574f47cf7ba26"
4175+
integrity sha1-BuL1E+YdaqNj1LVW8YV09Hz3uiY=
4176+
41654177
duplexer@^0.1.1:
41664178
version "0.1.1"
41674179
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"

0 commit comments

Comments
 (0)