工作中要做一个上传文件面板,初始化时面板部分有已上传文件列表。
根据ant-design设计原则
中解释刚刚发生了什么
原则,我想让新上传的文件行闪烁一下。
于是就有了下面的代码,新增了.blink的css,增加到新增行里去。现在准备出去玩,所以直截了当贴代码,(相关代码ctrl+f搜索'blink'就可以了):
<template>
<div class="documents-uploader-container">
<div class="documents-uploader-header">
{{loanDocument.name}}
</div>
<div class="documents-uploader-question-mark">
<i class="fa fa-question-circle" aria-hidden="true" v-b-popover.hover="popoverConfig"></i>
<span>{{loanDocument.description}}</span>
</div>
<div class="documents-uploader-area">
<button class="btn btn-primary" v-if="loanDocument.multiple">
Buscar
<input type="file" value="Buscar" :accept="loanDocument.type" multiple="multiple" @change="uploadHandler($event)" class="upload-file-input">
</button>
<button class="btn btn-primary" disabled v-if="!loanDocument.multiple">
Buscar
</button>
<!-- uploaded files -->
<div class="documents-uploader-documents-list" v-for="file in fileList">
<div class="uploaded-file-container" :class="{'blink':file.blink}" v-if="!file.uploading">
<span>{{file.name}}</span>
<div class="operation-btn">
<i class="fa fa-download" aria-hidden="true" @click="download(file)"></i>
<click-confirm style="font-size:24px" :messages="{title:'¿Seguro que quieres eliminarlo?',yes:'sí',no:'no'}">
<i class="fa fa-times-circle" aria-hidden="true" style="margin-left:10px" @click="deleteExistedFile(file)"></i>
</click-confirm>
</div>
</div>
<div class="uploading-file-container" v-if="file.uploading">
<div class="file-uploading-progress-bar" :style="{width:file.progress}"></div>
<div class="file-uploading-detail-wrapper">
<span>{{file.name}}</span>
<div class="operation-btn">
<click-confirm style="font-size:24px" :messages="{title:'¿Seguro que quieres eliminarlo?',yes:'sí',no:'no'}">
<i class="fa fa-times-circle" aria-hidden="true" style="margin-left:10px" @click="deleteExistedFile(file)"></i>
</click-confirm>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import { upload,download,remove } from '../utils/api.js'
let FormData = require('form-data');
import clickConfirm from 'click-confirm'
import 'vue-progress-path/dist/vue-progress-path.css'
import VueProgress from 'vue-progress-path'
Vue.use(VueProgress)
export default {
name: 'DocumentsUploader',
props: [ 'loanId','loanDocument','validationCallback','onError' ] ,
components: { clickConfirm,VueProgress },
data() {
return {
fileStack:[...this.loanDocument.files],
filesWaitingForUpload : [],
}
},
methods: {
uploadHandler(e){
let files = e.target.files
for(let i=0;i<files.length;i++){
let file = files[i]
let obj = {}
obj.name = file.name
obj.uploading = true
obj.progress = 0
this.fileStack.push(obj)
//formData to upload
let formData = new FormData();
formData.append('file', file);
let data = {}
data.loanId = this.loanId
data.docId = this.loanDocument.id
data.filename = file.name
data.file = formData
//callback to get upload progress
let callback = (progress)=>{
obj.progress = progress + "%"
}
upload(data,callback).then((response)=>{
setTimeout(()=>{
obj.uploading = false
this.validate()
obj.blink = true
},1500)
}).catch((err)=>{
this.onError()
})
}
},
deleteExistedFile(file){
for(let i=0;i<this.fileStack.length;i++){
let item = this.fileStack[i]
if(item.name === file.name || item === file.name){//"item === file.name" compatible for the backend just pass a array of filenames to the frontend
this.fileStack.splice(i,1)
}
}
let data = {}
data.loanId = this.loanId
data.docId = this.loanDocument.id
data.filename = file.name
return remove(data).then((res)=>{
}).catch((e)=>{
})
},
download(file){
let data = {}
data.loanId = this.loanId
data.docId = this.loanDocument.id
data.filename = file.name
return download(data).then((res)=>{
}).catch((e)=>{
})
},
validate(){
let docId = this.loanDocument.id
let checkUploaded = (file) => {
return file.uploading === false
}
if(docId === 'name_transfer_voucher'){//if docId == 'name_transfer_voucher', it need 12 documents to be compeleted.
this.fileList.filter(checkUploaded).length > 11 && this.validationCallback(docId)//tell parent compoent it is validated
}else {
this.fileList.filter(checkUploaded).length > 0 && this.validationCallback(docId)//tell parent compoent it is validated
}
}
},
computed: {
popoverConfig () {
return {
html: true,
content: () => { return this.loanDocument.text }
}
},
fileList(){//filelist is a computed array displayed in frontend
let arr = []
this.fileStack.map((item)=>{
let obj = {}
if(typeof item !== 'object'){//compatible for the backend just pass a array of filenames to the frontend
obj.name = item
obj.uploading = false
}else{
obj = item
}
arr.push(obj)
})
return arr
}
},
mounted(){
this.validate()
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
@-webkit-keyframes blink {
0% {
background-color: #eff1f6;
opacity:1;
}
50% {
background-color: #fefade;
}
100% {
background-color: #eff1f6;
}
}
.blink {
-webkit-animation-name: blink;
-webkit-animation-duration: 2000ms;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease-in-out;
}
>>> .vue-progress-path {
width: 40px !important;
height: 40px !important;
display: flex;
justify-content: center;
align-items: center;
}
>>> .vue-progress-path path {
stroke-width: 10;
}
>>> .vue-progress-path .progress {
stroke: #1f84be;
}
>>> .vue-progress-path .background {
stroke: #ddd;
}
.btn.btn-primary{
background: #1483c0;
background: #1483c0;
border:none;
display:flex;
justify-content:center;
align-items:center;
position:relative;
width:100px;
}
.documents-uploader-container{
padding: 0 15px;
}
.documents-uploader-header{
font-size: 20px;
padding: 15px 0 0;
font-weight: 500;
}
.documents-uploader-question-mark{
color:#fdc130;
display: flex;
align-items: center;
font-size: 20px;
padding: 15px 0;
}
.fa.fa-question-circle{
font-size: 30px;
}
.documents-uploader-area{
border: 1px solid #f2f2f2;
padding: 10px;
}
.documents-uploader-documents-list > div{
display: flex;
margin-top: 5px;
background: #eff1f6;
align-items: center;
color: #1483c0;
justify-content: space-between;
}
.documents-uploader-documents-list span{
/* flex:1; */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.operation-btn{
display: flex;
/* width: 80px; */
padding-left: 10px;
padding-right: 10px;
align-items: center;
/* justify-content: space-around; */
}
.operation-btn > i{
font-size: 25px;
transition: color 0.15s ease-in-out;
}
.operation-btn > i:active{
color: #0e6fc9;
}
.upload-file-input{
background:#f00;
position:absolute;
width:10px;
opacity:0;
left:0;
top:0;
height: 100%;
width:100%;
}
/* uploading part */
.uploaded-file-container{
padding: 10px 15px;
}
.uploading-file-container{
position:relative;
}
.file-uploading-progress-bar{
transition:width 1s ease;
position:absolute;
height:100%;
/* width:10%; */
background:#1f84be;
z-index:0
}
.file-uploading-detail-wrapper{
color:#fff;
padding: 10px 15px;
display:flex;
flex:1;
justify-content:space-between;
align-items:center;
flex-direction:row;
width:100%;
z-index:1;
}
/* uploading part end*/
</style>
网友评论