1+ <template >
2+ <div id =" global-uploader" >
3+ 4+ <!-- 上传 -->
5+ <uploader
6+ ref =" uploader"
7+ :options =" options"
8+ :autoStart =" false"
9+ @file-added =" onFileAdded"
10+ @file-success =" onFileSuccess"
11+ @file-progress =" onFileProgress"
12+ @file-error =" onFileError"
13+ class =" uploader-app" >
14+ <uploader-unsupport ></uploader-unsupport >
15+ 16+ <uploader-btn id =" global-uploader-btn" :attrs =" attrs" ref =" uploadBtn" >选择文件</uploader-btn >
17+ 18+ <uploader-list v-show =" panelShow" >
19+ <div class =" file-panel" slot-scope =" props" :class =" {'collapse': collapse}" >
20+ <div class =" file-title" >
21+ <h2 >文件列表</h2 >
22+ <div class =" operate" >
23+ <el-button @click =" fileListShow" type =" text" :title =" collapse ? '展开':'折叠' " >
24+ <i class =" iconfont" :class =" collapse ? 'inuc-fullscreen': 'inuc-minus-round'" ></i >
25+ </el-button >
26+ <el-button @click =" close" type =" text" title =" 关闭" >
27+ <i class =" iconfont icon-close" ></i >
28+ </el-button >
29+ </div >
30+ </div >
31+ 32+ <ul class =" file-list" >
33+ <li v-for =" file in props.fileList" :key =" file.id" >
34+ <uploader-file :class =" 'file_' + file.id" ref =" files" :file =" file" :list =" true" ></uploader-file >
35+ </li >
36+ <div class =" no-file" v-if =" !props.fileList.length" ><i class =" iconfont icon-empty-file" ></i > 暂无待上传文件</div >
37+ </ul >
38+ </div >
39+ </uploader-list >
40+ 41+ </uploader >
42+ 43+ </div >
44+ </template >
45+ 46+ <script >
47+ import {ACCEPT_CONFIG } from ' ./js/config' ;
48+ import Bus from ' ./js/bus' ;
49+ import SparkMD5 from ' spark-md5' ;
50+
51+ // 这两个是我自己项目中用的,请忽略
52+ import {Ticket } from ' @/assets/js/utils' ;
53+ import api from ' @/api' ;
54+
55+ export default {
56+ data () {
57+ return {
58+ options: {
59+ target: api .simpleUploadURL ,
60+ chunkSize: ' 2048000' ,
61+ fileParameterName: ' upfile' ,
62+ testChunks: true , // 是否开启秒传
63+ maxChunkRetries: 3 ,
64+ checkChunkUploadedByResponse : function (chunk , message ) {
65+ let objMessage = JSON .parse (message);
66+ if (objMessage .skipUpload ) {
67+ return true ;
68+ }
69+
70+ return (objMessage .uploaded || []).indexOf (chunk .offset + 1 ) >= 0
71+ },
72+ headers: {
73+ Authorization: Ticket .get () && " Bearer " + Ticket .get ().access_token
74+ },
75+ query () {
76+ }
77+ },
78+ attrs: {
79+ accept: ACCEPT_CONFIG .getAll ()
80+ },
81+ panelShow: false , // 选择文件后,展示上传panel
82+ collapse: false ,
83+ }
84+ },
85+ created () {
86+ },
87+ mounted () {
88+ Bus .$on (' openUploader' , query => {
89+ this .params = query || {};
90+
91+ if (this .$refs .uploadBtn ) {
92+ $ (' #global-uploader-btn' ).click ();
93+ }
94+ });
95+ },
96+ computed: {
97+ // Uploader实例
98+ uploader () {
99+ return this .$refs .uploader .uploader ;
100+ }
101+ },
102+ methods: {
103+ onFileAdded (file ) {
104+ this .panelShow = true ;
105+ this .computeMD5 (file);
106+ },
107+ onFileProgress (rootFile , file , chunk ) {
108+ console .log (` 上传中 ${ file .name } ,chunk:${ chunk .startByte / 1024 / 1024 } ~ ${ chunk .endByte / 1024 / 1024 } ` )
109+ },
110+ onFileSuccess (rootFile , file , response , chunk ) {
111+ let res = JSON .parse (response);
112+
113+ // 服务器自定义的错误,这种错误是Uploader无法拦截的
114+ if (! res .result ) {
115+ this .$message ({ message: res .message , type: ' error' });
116+ return
117+ }
118+
119+ // 如果服务端返回需要合并
120+ if (res .needMerge ) {
121+ api .mergeSimpleUpload ({
122+ tempName: res .tempName ,
123+ fileName: file .name ,
124+ ... this .params ,
125+ }).then (res => {
126+ // 文件合并成功
127+ Bus .$emit (' fileSuccess' );
128+ }).catch (e => {});
129+
130+ // 不需要合并
131+ } else {
132+ Bus .$emit (' fileSuccess' );
133+ console .log (' 上传成功' );
134+ }
135+ },
136+ onFileError (rootFile , file , response , chunk ) {
137+ this .$message ({
138+ message: response,
139+ type: ' error'
140+ })
141+ },
142+
143+ /**
144+ * 计算md5,实现断点续传及秒传
145+ * @param file
146+ */
147+ computeMD5 (file ) {
148+ let fileReader = new FileReader ();
149+ let time = new Date ().getTime ();
150+ let md5 = ' ' ;
151+
152+ file .pause ();
153+
154+ fileReader .readAsArrayBuffer (file .file );
155+
156+ fileReader .onload = (e => {
157+ if (file .size != e .target .result .byteLength ) {
158+ this .error (' Browser reported success but could not read the file until the end.' );
159+ return
160+ }
161+
162+ md5 = SparkMD5 .ArrayBuffer .hash (e .target .result );
163+
164+ // 添加额外的参数
165+ this .uploader .opts .query = {
166+ ... this .params
167+ }
168+
169+ console .log (` MD5计算完毕:${ file .id } ${ file .name } MD5:${ md5} 用时:${ new Date ().getTime () - time} ms` );
170+
171+ file .uniqueIdentifier = md5;
172+ file .resume ();
173+ });
174+
175+ fileReader .onerror = function () {
176+ this .error (' FileReader onerror was triggered, maybe the browser aborted due to high memory usage.' );
177+ };
178+ },
179+
180+ fileListShow () {
181+ let $list = $ (' #global-uploader .file-list' );
182+
183+ if ($list .is (' :visible' )) {
184+ $list .slideUp ();
185+ this .collapse = true ;
186+ } else {
187+ $list .slideDown ();
188+ this .collapse = false ;
189+ }
190+ },
191+ close () {
192+ this .uploader .cancel ();
193+
194+ this .panelShow = false ;
195+ },
196+
197+ error (msg ) {
198+ this .$notify ({
199+ title: this .$t (' c.false' ),
200+ message: msg,
201+ type: ' error' ,
202+ duration: 2000
203+ })
204+ }
205+ },
206+ watch: {},
207+ destroyed () {
208+ Bus .$off (' openUploader' );
209+ },
210+ components: {}
211+ }
212+ </script >
213+ 214+ <style scoped lang="scss">
215+ #global-uploader {
216+ position : fixed ;
217+ z-index : 20 ;
218+ right : 15px ;
219+ bottom : 15px ;
220+
221+ .uploader-app {
222+ width : 520px ;
223+ }
224+
225+ .file-panel {
226+ background-color : #fff ;
227+ border : 1px solid #e2e2e2 ;
228+ border-radius : 7px 7px 0 0 ;
229+ box-shadow : 0 0 10px rgba (0 , 0 , 0 , .2 );
230+
231+ .file-title {
232+ display : flex ;
233+ height : 40px ;
234+ line-height : 40px ;
235+ padding : 0 15px ;
236+ border-bottom : 1px solid #ddd ;
237+
238+ .operate {
239+ flex : 1 ;
240+ text-align : right ;
241+ }
242+ }
243+
244+ .file-list {
245+ position : relative ;
246+ height : 240px ;
247+ overflow-x : hidden ;
248+ overflow-y : auto ;
249+ background-color : #fff ;
250+
251+ > li {
252+ background-color : #fff ;
253+ }
254+ }
255+
256+ & .collapse {
257+ .file-title {
258+ background-color : #E7ECF2 ;
259+ }
260+ }
261+ }
262+
263+ .no-file {
264+ position : absolute ;
265+ top : 50% ;
266+ left : 50% ;
267+ transform : translate (-50% , -50% );
268+ font-size : 16px ;
269+ }
270+
271+ /deep /.uploader-file-icon {
272+ & :before {
273+ content : ' ' !important ;
274+ }
275+
276+ & [icon = image ] {
277+ background : url (./images/image-icon.png );
278+ }
279+ & [icon = video ] {
280+ background : url (./images/video-icon.png );
281+ }
282+ & [icon = document ] {
283+ background : url (./images/text-icon.png );
284+ }
285+ }
286+
287+ /deep /.uploader-file-actions > span {
288+ margin-right : 6px ;
289+ }
290+ }
291+
292+ /* 隐藏上传按钮 */
293+ #global-uploader-btn {
294+ position : absolute ;
295+ clip : rect (0 , 0 , 0 , 0 );
296+ }
297+ </style >
0 commit comments