2/17 Vue 上传图片并预览的实现 ( 完 )
写在前面
之前我写了一个刚刚好的没有涉及到提交方面的问题的 html
大家可以参考一下 ( 直接复制就能用 )
https://www.cnblogs.com/WaterMealone/p/14407875.html
今天连着 express 暂时把这个组件搞出来了
有些关于底层的问题 我暂时解决不了 只能曲线救组件....
组件如何引用
暂时还没有涉及到 userId 的部分只是先打了一个预防针而已
然后就是一个“玩具”变量 就是上传的大小限制
演示的动画
一些变量
我写的时候,没有仔细思考,只是看着想加一个就弄一个,然后这个组件的逻辑可能不是
很清晰
我先把一些变量说一下
1.父组件的参数
这个应该不用难理解
2.data
1 props: { 2 userId: { 3 type: String, 4 validator(value) { 5 // 判断String是否为空 6 return value === "" ? false : true; 7 }, 8 default: "", 9 }, 10 11 maxSize: { 12 type: Number, 13 validator(value) { 14 return value > 0 ? true : false; 15 }, 16 default: 10, 17 }, 18 }, 19 data() { 20 return { 21 // 预展示的图片对象数组 22 preFiles: [], 23 24 // 渲染列表 25 preFilesLength: 0, 26 27 // 这个是渲染list的过程 渲染到第几个li 28 updateState: 0, 29 30 // 真正要上传的图片文件 31 files: [], 32 33 // 选择上传的文件 之前想到了更改 但是没有成功 就留下一个响应的变量 可以删除的 34 select: "全选", 35 36 // 是否进行拖动事件 37 isDrag: false, 38 39 // 上传进度 0 表示还未开始 1 表示开始上传 2 表示结束上传 40 uploadStatu: 0, 41 }; 42 },
开始写代码
首先,我们得确定怎么来弄这个东西
我最初的想法就是,就是普通的上传,可以拖文件上传,然后显示预览,然后选择自己想要上传的文件
...
然后我先把最初的还没有涉及拖动文件的那一部分,先弄了出来 然后缝缝补补开始弄
1.第一部分
其实这里这个部分最主要的就是对于 label 标签的运用
( 我点击这个 div 相当于点击的是 input )
这里有一个方法,当 input 标签变化的时候传入事件的 target ( 可以自己看一下 事件 的target )
storepreFiles(obj) { // 如果当前的上传状态是 0-未上传 if (this.uploadStatu === 0) { // 获取input里面的文件组 var fileList = obj.files; // 先判定有没有重复提交的文件 可以加判断条件 for (let i = 0; i < fileList.length; i++) { for (let j = 0; j < this.preFiles.length; j++) { if (this.preFiles[j].name === fileList[i].name) { alert("有文件重复"); return; } } } // 这个是处理 显示 有多少个文件在列表的情况 ( 因为可以在未上传的时候 继续添加 ) this.preFilesLength = this.preFilesLength + fileList.length; // 进行整合 ( 如果直接用push的话 这样会变成 两个Filelist 对象 组合 ) for (var i = 0; i < fileList.length; i++) { this.files = this.files.concat(fileList[i]); } // 下面的作用域会变 var vue = this; // 对文件组进行遍历,可以到控制台打印出fileList去看看 // for里面的执行机制很奇怪 这里经过验证用了 promise 但是 还是发现写入的时候, 执行顺序是乱的 for (let i = 0; i < fileList.length; i++) { function uploadFile(file) { return new Promise(function (resolve, reject) { let reader = new FileReader(); // 将file(图片) 读取为一个 DataUrl 可以直接用 img 里面的 src 进行展示 reader.readAsDataURL(file); reader.onload = function () { resolve(this.result); }; }); } uploadFile(fileList[i]).then(function (result) { console.group(i); console.log(event); console.log(fileList[i]); console.groupEnd(); // vue.preFiles.splice(i, 0, { name: fileList[i].name, size: fileList[i].size, index: 0, }) vue.preFiles.push({ name: fileList[i].name, size: fileList[i].size, index: 0, src: result, isChecked: true, }); }); } } else { alert("已经上传完毕!"); } },
2.第二部分
首先我们得确定现在是 不是 为上传状态 非0 或者是 files || preFiles 里面 不是空的
其实上面的那个 storepreFiles 的主要目的 就是存储一个 预览文件图片的对象 数组 就是 preFiles
html 中 有些地方 可以对照着看
比如加载进度条这个地方 其实是对 preFiles 进行了 watch
当 preFiles 发生改变 进行 侦听
先 “过滤” 并不是这个时候的更新条件( 比如只是数组内一个对象属性的改变 必须得过滤 我们需要的是整个数组中元素的增加或者减少 ),然后才进行操作
这里不过于细写 因为牵扯到后面的内容了
<div class="show-file-box" v-if="preFiles.length == 0 ? false : true"> <div class="text-container"> <span>上传列表</span> <span>{{ updateState }} / {{ preFilesLength }}</span> </div> <!-- 渲染出来的情况 有一个进度条 --> <div class="pre-upload-bar"> <div> <span >列表加载进度 {{ (updateState / preFilesLength).toFixed(1) * 100 }} %</span > </div> <div class="pre-upload-bar-done" :style="{ width: (updateState / preFilesLength) * 100 + '%' }" ></div> </div> <!-- <div v-if="updateState === preFilesLength ? true : false"> --> <ul class="pre-upload-list"> <transition-group tag="li" name="list"> <li class="pre-upload-file" v-for="file in preFiles" :key="file.name" > <div class="review"> <!-- 图片预览 --> <div class="review-image-container"> <img class="review-image" :src="file.src" /> </div> <div class="review-file-name-container"> <span class="file-name"> {{ file.name }}</span> </div> <div class="progress"> <div class="progress-done" :style="{ width: file.index + '%' }" ></div> </div> <span class="percent">{{ file.index }}%</span> <input v-if="uploadStatu === 0 ? true : false" class="select-box" check type="checkbox" name="select" :value="file.name" :checked="file.isChecked" @change="doSelect(file.name)" ref="select" /> </div> </li> </transition-group> </ul>
3.第三部分
这个地方 其实是最简单的地方,只需要对我们现存的 preFiles 数组里面的对象的属性进行操作 就行了 ( 全选按钮 )
1 <hr color="lightpink" style="margin: 10px 8px" /> 2 <!-- 选择按钮 --> 3 <div class="bottom"> 4 <div 5 class="bottom-shelter" 6 v-if="updateState != preFilesLength" 7 ></div> 8 <div class="bottom-content"> 9 <div class="bottom-select"> 10 <span 11 class="bottom-select-button" 12 @click="seletAll" 13 v-if="uploadStatu === 0 ? true : false" 14 > 15 {{ select }}</span 16 > 17 <div class="bottom-select-info"> 18 <div> 19 <span>已选</span> 20 <span 21 class="span-special" 22 :style="{ color: showSize() > maxSize ? 'red' : 'green' }" 23 >{{ showSelect() }} 24 </span> 25 <span>/{{ preFilesLength }}</span> 26 </div> 27 <div> 28 <span 29 class="span-special" 30 :style="{ color: showSize() > maxSize ? 'red' : 'green' }" 31 >{{ showSize() }} 32 </span> 33 <span>/ {{ showAllSize() }} mb </span> 34 </div> 35 </div> 36 </div> 37 38 <div 39 class="upload-statu-info" 40 v-if="uploadStatu == 0 ? false : true" 41 > 42 <span>{{ uploadStatu == 1 ? "上传ing" : "" }}</span> 43 <span>{{ uploadStatu == 2 ? "上传完成" : "" }}</span> 44 </div> 45 46 <div 47 class="bottom-upload" 48 @click="upload" 49 v-if="uploadStatu === 0 ? true : false" 50 > 51 <span>上传</span> 52 </div> 53 54 <div 55 class="bottom-upload" 56 @click="continueUpload" 57 v-if="uploadStatu === 2 ? true : false" 58 > 59 <span>清空列表</span> 60 </div> 61 </div> 62 </div>
全部代码
简单的组成就是这样,但是细说肯定很难说,推荐有缘人直接拿着我的代码 注册一个组件看
1 <template> 2 <div id="app"> 3 <div class="container"> 4 <div class="title-container"> 5 <span>上传文件</span> 6 </div> 7 8 <div class="upload-box"> 9 <!-- 上传的那个框框 --> 10 <div 11 class="drag-box" 12 ref="dragBox" 13 :class="isDrag ? 'draging-drag-box' : ''" 14 > 15 <label> 16 <div class="icon-container"> 17 <i 18 class="fa fa-file" 19 :class="isDrag ? 'draging-fa-file' : ''" 20 ></i> 21 </div> 22 <div class="label-text-container"> 23 <span :class="isDrag ? 'draging-label-text-container' : ''" 24 >请将文件拖动至此</span 25 > 26 <br /> 27 <span :class="isDrag ? 'draging-label-text-container' : ''" 28 >或者单击上传</span 29 > 30 </div> 31 <input 32 type="file" 33 accept="image/jepg, image/png" 34 name="pictures" 35 @change="storepreFiles($event.target)" 36 multiple 37 /> 38 </label> 39 </div> 40 41 <div class="show-file-box" v-if="preFiles.length == 0 ? false : true"> 42 <div class="text-container"> 43 <span>上传列表</span> 44 <span>{{ updateState }} / {{ preFilesLength }}</span> 45 </div> 46 <!-- 渲染出来的情况 有一个进度条 --> 47 <div class="pre-upload-bar"> 48 <div> 49 <span 50 >列表加载进度 51 {{ (updateState / preFilesLength).toFixed(1) * 100 }} %</span 52 > 53 </div> 54 <div 55 class="pre-upload-bar-done" 56 :style="{ width: (updateState / preFilesLength) * 100 + '%' }" 57 ></div> 58 </div> 59 <!-- <div v-if="updateState === preFilesLength ? true : false"> --> 60 61 <ul class="pre-upload-list"> 62 <transition-group tag="li" name="list"> 63 <li 64 class="pre-upload-file" 65 v-for="file in preFiles" 66 :key="file.name" 67 > 68 <div class="review"> 69 <!-- 图片预览 --> 70 <div class="review-image-container"> 71 <img class="review-image" :src="file.src" /> 72 </div> 73 74 <div class="review-file-name-container"> 75 <span class="file-name"> {{ file.name }}</span> 76 </div> 77 <div class="progress"> 78 <div 79 class="progress-done" 80 :style="{ width: file.index + '%' }" 81 ></div> 82 </div> 83 <span class="percent">{{ file.index }}%</span> 84 <input 85 v-if="uploadStatu === 0 ? true : false" 86 class="select-box" 87 check 88 type="checkbox" 89 name="select" 90 :value="file.name" 91 :checked="file.isChecked" 92 @change="doSelect(file.name)" 93 ref="select" 94 /> 95 </div> 96 </li> 97 </transition-group> 98 </ul> 99 100 <hr color="lightpink" style="margin: 10px 8px" /> 101 <!-- 选择按钮 --> 102 <div class="bottom"> 103 <div 104 class="bottom-shelter" 105 v-if="updateState != preFilesLength" 106 ></div> 107 <div class="bottom-content"> 108 <div class="bottom-select"> 109 <span 110 class="bottom-select-button" 111 @click="seletAll" 112 v-if="uploadStatu === 0 ? true : false" 113 > 114 {{ select }}</span 115 > 116 <div class="bottom-select-info"> 117 <div> 118 <span>已选</span> 119 <span 120 class="span-special" 121 :style="{ color: showSize() > maxSize ? 'red' : 'green' }" 122 >{{ showSelect() }} 123 </span> 124 <span>/{{ preFilesLength }}</span> 125 </div> 126 <div> 127 <span 128 class="span-special" 129 :style="{ color: showSize() > maxSize ? 'red' : 'green' }" 130 >{{ showSize() }} 131 </span> 132 <span>/ {{ showAllSize() }} mb </span> 133 </div> 134 </div> 135 </div> 136 137 <div 138 class="upload-statu-info" 139 v-if="uploadStatu == 0 ? false : true" 140 > 141 <span>{{ uploadStatu == 1 ? "上传ing" : "" }}</span> 142 <span>{{ uploadStatu == 2 ? "上传完成" : "" }}</span> 143 </div> 144 145 <div 146 class="bottom-upload" 147 @click="upload" 148 v-if="uploadStatu === 0 ? true : false" 149 > 150 <span>上传</span> 151 </div> 152 153 <div 154 class="bottom-upload" 155 @click="continueUpload" 156 v-if="uploadStatu === 2 ? true : false" 157 > 158 <span>清空列表</span> 159 </div> 160 </div> 161 </div> 162 </div> 163 </div> 164 </div> 165 </div> 166 </template> 167 168 <script> 169 export default { 170 name: "upload", 171 props: { 172 userId: { 173 type: String, 174 validator(value) { 175 // 判断String是否为空 176 return value === "" ? false : true; 177 }, 178 default: "", 179 }, 180 181 maxSize: { 182 type: Number, 183 validator(value) { 184 return value > 0 ? true : false; 185 }, 186 default: 10, 187 }, 188 }, 189 data() { 190 return { 191 // 预展示的图片对象数组 192 preFiles: [], 193 194 // 渲染列表 195 preFilesLength: 0, 196 197 // 这个是渲染list的过程 渲染到第几个li 198 updateState: 0, 199 200 // 真正要上传的图片文件 201 files: [], 202 203 // 选择上传的文件 之前想到了更改 但是没有成功 就留下一个响应的变量 可以删除的 204 select: "全选", 205 206 // 是否进行拖动事件 207 isDrag: false, 208 209 // 上传进度 0 表示还未开始 1 表示开始上传 2 表示结束上传 210 uploadStatu: 0, 211 }; 212 }, 213 updated() {}, 214 mounted: function () { 215 var dragBox = this.$refs.dragBox; 216 dragBox.addEventListener("dragenter", this.onDrag, false); 217 dragBox.addEventListener("dragover", this.onDrag, false); 218 dragBox.addEventListener( 219 "dragleave", 220 () => { 221 this.isDrag = false; 222 }, 223 false 224 ); 225 dragBox.addEventListener("drop", this.onDrop, false); 226 }, 227 watch: { 228 // 通过对 preFiles 监听 实现简单加载文件进度条 229 preFiles: { 230 handler: "updateS", 231 }, 232 }, 233 methods: { 234 storepreFiles(obj) { 235 // 如果当前的上传状态是 0-未上传 236 if (this.uploadStatu === 0) { 237 // 获取input里面的文件组 238 var fileList = obj.files; 239 // 先判定有没有重复提交的文件 可以加判断条件 240 for (let i = 0; i < fileList.length; i++) { 241 for (let j = 0; j < this.preFiles.length; j++) { 242 if (this.preFiles[j].name === fileList[i].name) { 243 alert("有文件重复"); 244 return; 245 } 246 } 247 } 248 249 // 这个是处理 显示 有多少个文件在列表的情况 ( 因为可以在未上传的时候 继续添加 ) 250 this.preFilesLength = this.preFilesLength + fileList.length; 251 252 // 进行整合 ( 如果直接用push的话 这样会变成 两个Filelist 对象 组合 ) 253 for (var i = 0; i < fileList.length; i++) { 254 this.files = this.files.concat(fileList[i]); 255 } 256 257 // 下面的作用域会变 258 var vue = this; 259 // 对文件组进行遍历,可以到控制台打印出fileList去看看 260 // for里面的执行机制很奇怪 这里经过验证用了 promise 但是 还是发现写入的时候, 执行顺序是乱的 261 for (let i = 0; i < fileList.length; i++) { 262 function uploadFile(file) { 263 return new Promise(function (resolve, reject) { 264 let reader = new FileReader(); 265 // 将file(图片) 读取为一个 DataUrl 可以直接用 img 里面的 src 进行展示 266 reader.readAsDataURL(file); 267 reader.onload = function () { 268 resolve(this.result); 269 }; 270 }); 271 } 272 uploadFile(fileList[i]).then(function (result) { 273 console.group(i); 274 console.log(event); 275 console.log(fileList[i]); 276 console.groupEnd(); 277 // vue.preFiles.splice(i, 0, { name: fileList[i].name, size: fileList[i].size, index: 0, }) 278 vue.preFiles.push({ 279 name: fileList[i].name, 280 size: fileList[i].size, 281 index: 0, 282 src: result, 283 isChecked: true, 284 }); 285 }); 286 } 287 } else { 288 alert("已经上传完毕!"); 289 } 290 }, 291 292 // 渲染列表的参数 293 updateS() { 294 if (this.updateState >= this.preFilesLength) { 295 // this.preFilesLength = this.preFilesLength - this.preFiles.filter(item => item.isChecked == false).length; 296 this.updateState = 297 this.updateState - (this.preFilesLength - this.preFiles.length); 298 } else { 299 this.updateState = this.updateState + 1; 300 } 301 }, 302 303 findPreFile(name) { 304 let that = this; 305 return this.preFiles.find((item, index) => { 306 return item.name === name; 307 }); 308 }, 309 310 // 点击 选择框 和 全选 311 doSelect(name) { 312 this.preFiles.forEach((element) => { 313 if (element.name == name) { 314 element.isChecked = !element.isChecked; 315 } 316 }); 317 }, 318 seletAll() { 319 if (this.$refs.select.find((item) => item.checked == false)) { 320 console.log(this.$refs.select); 321 this.preFiles.forEach((element) => { 322 element.isChecked = true; 323 }); 324 } 325 }, 326 327 // 显示 选中个数 和 显示 选中的文件大小 328 showSelect() { 329 let num = 0; 330 this.preFiles.forEach((element) => { 331 if (element.isChecked == true) { 332 num++; 333 } 334 }); 335 return num; 336 }, 337 338 showSize() { 339 let size = 0; 340 this.preFiles.forEach((element) => { 341 if (element.isChecked == true) { 342 size = size + element.size; 343 } 344 }); 345 return (size / Math.pow(2, 20)).toFixed(2); 346 }, 347 348 showAllSize() { 349 let size = 0; 350 this.preFiles.forEach((element) => { 351 size = size + element.size; 352 }); 353 return (size / Math.pow(2, 20)).toFixed(2); 354 }, 355 356 // 拖动文件上传 357 onDrag: function (e) { 358 this.isDrag = true; 359 // 取消默认事件 360 e.stopPropagation(); 361 e.preventDefault(); 362 }, 363 364 onDragLeave(e) {}, 365 366 onDrop: function (e) { 367 this.isDrag = false; 368 e.stopPropagation(); 369 e.preventDefault(); 370 var dt = e.dataTransfer; 371 this.storepreFiles(dt); 372 }, 373 374 // 继续文件上传 慎重使用这个方法 375 continueUpload() { 376 this.uploadStatu = 0; 377 this.preFiles = []; 378 this.files = []; 379 this.preFilesLength = this.files.length; 380 this.updateState = 0; 381 }, 382 // 文件上传 383 // 确定上传文件 通过 preFiles 的 isChecked 属性 取得每个文件的 name 和 真正要上传的文件的 name 交叉对比 384 checkFiles() { 385 console.log(this.preFiles); 386 if ( 387 this.preFiles != this.preFiles.filter((item) => item.isChecked == true) 388 ) { 389 this.preFiles = this.preFiles.filter((item) => item.isChecked == true); 390 } 391 let vue = this; 392 function thisFirst() { 393 return new Promise(function (resolve, reject) { 394 for (let i = 0; i < vue.files.length; i++) { 395 var that = vue; 396 function first(i, j) { 397 return new Promise(function (resolve, reject) { 398 let flag = 1; 399 for (let j = 0; j < vue.preFiles.length; j++) { 400 if (that.files[i].name === that.preFiles[j].name) { 401 flag = flag * 0; 402 } else { 403 flag = flag * 1; 404 } 405 console.group(i); 406 console.log(that.files[i].name); 407 console.log(that.preFiles[j].name); 408 console.log(flag + " " + i); 409 console.log({ flag, i }); 410 console.groupEnd(); 411 } 412 resolve({ flag: flag, i: i }); 413 }); 414 } 415 416 first(i).then((obj) => { 417 console.log(obj); 418 if (obj.flag != 0) { 419 delete that.files[obj.i]; 420 } 421 }); 422 // } 423 } 424 resolve(); 425 }); 426 } 427 428 thisFirst().then(() => { 429 vue.files = vue.files.filter((item) => item != "undefined"); 430 vue.preFilesLength = vue.files.length; 431 vue.updateState = vue.files.length; 432 }); 433 }, 434 upload() { 435 if (this.showSize() == 0) { 436 alert("您 啥也没选 选个毛"); 437 } 438 if (this.maxSize < this.showSize()) { 439 alert("文件太大!请重新选择!"); 440 } else { 441 let vue = this; 442 function firstDo() { 443 return new Promise(function (resolve, reject) { 444 if (vue.preFilesLength != 0) { 445 vue.checkFiles(); 446 vue.preFilesLength = vue.files.length; 447 console.log("updating " + vue.preFiles); 448 } 449 resolve(); 450 }); 451 } 452 453 firstDo().then(() => { 454 vue.files.forEach((element) => { 455 vue.uploadSingleFile(element); 456 }); 457 vue.uploadStatu = 2; 458 }); 459 } 460 }, 461 462 uploadSingleFile(file) { 463 // 先找到匹配的 预览文件 位置 方便写入 index 464 465 let position = 0; 466 this.preFiles.forEach((element) => { 467 if (element.name == file.name) { 468 position = this.preFiles.indexOf(element); 469 } 470 }); 471 472 this.uploadStatu = 1; 473 474 let param = new FormData(); // 创建form对象 475 param.append("file", file); // 通过append向form对象添加数据 476 console.log(param.get("file")); // FormData私有类对象,访问不到,可以通过get判断值是否传进去 477 let vue = this; 478 vue.axios 479 .post("/demo/upload_test", param, { 480 // 给个请求头,让后端知道应该怎么处理数据 481 headers: { 482 "Content-Type": "multipart/form-data", 483 }, 484 onUploadProgress: (progressEvent) => { 485 let processStatu = 486 ((progressEvent.loaded / progressEvent.total) * 100) | 0; 487 vue.preFiles[position].index = processStatu; 488 }, 489 }) 490 .then((response) => { 491 console.log(response.data); 492 }); 493 }, 494 }, 495 }; 496 </script> 497 498 <style scoped> 499 span { 500 font-weight: 700; 501 font: bolder; 502 } 503 504 .container { 505 width: 450px; 506 /* background-color: rgb(195, 209, 228); */ 507 box-shadow: 2px 2px 2px 2px rgba(68, 68, 68, 0.2); 508 border-radius: 10px; 509 padding: 20px; 510 } 511 512 .upload-box { 513 /* width: 400px; */ 514 /* height: 400px; */ 515 } 516 517 .title-container { 518 margin: 10px 8px 20px 8px; 519 font-size: 20px; 520 521 border-radius: 5px; 522 background: linear-gradient(to right, rgb(112, 158, 242), rgb(255, 255, 255)); 523 color: white; 524 } 525 526 .drag-box { 527 padding: 20px; 528 margin: 10px auto; 529 width: 380px; 530 border-radius: 10px; 531 border: 5px dashed rgba(112, 155, 248, 0.7); 532 text-align: center; 533 transition: all linear 0.1s; 534 } 535 536 .label-text-container { 537 margin: 20px 8px; 538 font-size: 20px; 539 } 540 541 .text-container { 542 margin: 20px 8px; 543 font-size: 20px; 544 border-radius: 5px; 545 background: linear-gradient(to right, rgb(112, 158, 242), rgb(255, 255, 255)); 546 color: white; 547 } 548 549 .drag-box .label-text-container { 550 color: rgba(190, 190, 190, 0.8); 551 transition: all linear 0.1s; 552 } 553 554 .icon-container { 555 margin: 15px; 556 } 557 558 .fa-file { 559 color: rgba(247, 187, 219, 0.8); 560 font-size: 100px; 561 transition: all linear 0.1s; 562 } 563 564 /* 当进入文件的时候添加css */ 565 .drag-box:hover { 566 border: 5px dashed rgba(112, 155, 248, 1); 567 } 568 569 .drag-box:hover .fa-file { 570 color: rgb(245, 124, 188); 571 } 572 573 .drag-box:hover .label-text-container { 574 color: rgb(68, 68, 68); 575 } 576 577 /* 当拖动文件的时候添加css */ 578 .draging-drag-box { 579 border: 5px dashed rgba(112, 155, 248, 1); 580 } 581 582 .draging-fa-file { 583 color: rgb(245, 124, 188); 584 } 585 586 .draging-label-text-container { 587 color: rgb(68, 68, 68); 588 } 589 590 /* 点击上传文件的时候的那个input */ 591 label input { 592 display: none; 593 } 594 595 .review { 596 border: 1px solid transparent; 597 border-radius: 5px; 598 color: #777; 599 display: flex; 600 font-size: 12px; 601 align-items: center; 602 padding: 10px; 603 margin: 5px 8px; 604 } 605 606 .review:hover { 607 cursor: pointer; 608 /* border: 1px solid #ddd; */ 609 box-shadow: 0 3px 10px -5px rgba(0, 0, 0, 0.7); 610 } 611 612 .pre-upload-bar { 613 margin: 10px 10px; 614 height: 20px; 615 } 616 617 .pre-upload-bar span { 618 font-size: 12px; 619 font-weight: 20px; 620 color: #777; 621 } 622 623 .pre-upload-bar-done { 624 background: linear-gradient(to left, rgb(112, 158, 242), rgb(119, 140, 255)); 625 box-shadow: 0 3px 3px -5px rgb(100, 115, 143), rgb(134, 138, 165); 626 border-radius: 5px; 627 height: 3px; 628 width: 0; 629 transition: width ease 0.2s; 630 } 631 632 .review-file-name-container { 633 width: 100px; 634 } 635 636 .review-image-container { 637 margin: 0 8px 0 0; 638 } 639 640 .review-image { 641 width: 50px; 642 } 643 644 .pre-upload-list { 645 list-style: none; 646 /* 取消缩进 */ 647 margin: 0px; 648 padding: 0px; 649 } 650 651 .file-name { 652 width: 100%; 653 float: left; 654 overflow: hidden; 655 text-overflow: ellipsis; 656 white-space: normal; 657 } 658 659 .progress { 660 background-color: rgba(100, 100, 100, 0.2); 661 border-radius: 5px; 662 position: relative; 663 margin: 0 10px; 664 height: 10px; 665 width: 150px; 666 } 667 668 .progress-done { 669 background: linear-gradient(to left, rgb(242, 112, 156), rgb(255, 148, 114)); 670 box-shadow: 0 3px 3px -5px rgb(242, 112, 156), 0 2px 5px rgb(242, 112, 156); 671 border-radius: 5px; 672 height: 10px; 673 width: 0; 674 transition: width ease 0.1s; 675 } 676 677 .select-box { 678 width: 20px; 679 height: 20px; 680 margin: 0 0 0 36px; 681 } 682 683 /* list的transition group */ 684 .list-enter, 685 .list-leave-to { 686 opacity: 0; 687 } 688 689 .list-enter-active { 690 animation: moveIn 1s; 691 } 692 693 .list-leave-active { 694 animation: moveOut 1s; 695 /* transition: all 1s linear; */ 696 } 697 698 @keyframes moveIn { 699 0% { 700 opacity: 0; 701 transform: translate(30px, 15px); 702 } 703 704 30% { 705 opacity: 0.5; 706 transform: translate(0px, 15px); 707 } 708 709 100% { 710 opacity: 1; 711 transform: translate(0, 0px); 712 } 713 } 714 715 @keyframes moveOut { 716 0% { 717 opacity: 1; 718 transform: translate(0, 0px); 719 } 720 721 30% { 722 opacity: 0.5; 723 transform: translate(0px, 15px); 724 } 725 726 100% { 727 opacity: 0; 728 transform: translate(30px, 15px); 729 } 730 } 731 .upload-statu-info { 732 padding: 0 10px; 733 margin: 3px 8px; 734 } 735 .upload-statu-info span { 736 letter-spacing: 5px; 737 font-weight: 300px; 738 } 739 740 .bottom-select { 741 margin: 5px 8px; 742 padding: 0 10px; 743 height: 40px; 744 } 745 746 .bottom-select .bottom-select-button { 747 cursor: pointer; 748 color: white; 749 margin: 5px; 750 padding: 7px; 751 float: right; 752 transition: all linear 0.1s; 753 background-color: rgb(247, 159, 172); 754 border-radius: 5px; 755 } 756 757 .bottom-select-info span { 758 font-size: 15px; 759 font-weight: 200; 760 } 761 762 .bottom-select-info .span-special { 763 font-weight: 1000; 764 } 765 766 .bottom-upload { 767 margin: 5% auto; 768 width: 160px; 769 height: 40px; 770 text-align: center; 771 padding: 5px; 772 border-radius: 5px; 773 background-color: lightpink; 774 color: white; 775 cursor: pointer; 776 } 777 778 .bottom-upload span { 779 letter-spacing: 5px; 780 margin: auto; 781 font-size: 30px; 782 } 783 784 .bottom { 785 position: relative; 786 width: 450px; 787 height: 150px; 788 } 789 790 .bottom-shelter { 791 position: absolute; 792 width: 100%; 793 height: 150px; 794 cursor: not-allowed; 795 z-index: 1; 796 } 797 798 .bottom-content { 799 width: 100%; 800 position: absolute; 801 z-index: 0; 802 } 803 </style>
总结
这个代码其实还是有些地方有问题的,比如 for 循环在用 fileReader 将图片读为 dataUrl 格式的时候 preFiles 的写入顺序 并不是按照for循环的顺序
对此我百思不得其解
只能靠后期对 数组遍历来解决问题
希望以后对js有了一些新的看法才来 更新一下
最后
我终于写完了
当然这个只是一个开始
还有后端的接收的地方,那一段代码 这个可以看我前面写的一些随笔 我懒得翻了