vue2组件库
上传组件
核心思路:监控整个上传的流程
上传成功 上传失败
类型:拖拽 多个文件上传
上传必备属性 & 钩子属性
跟上传强关联的属性,上传必备的字段
name: 提交的那个formData字段名
action:ajax接口路径
limit:限制提交个数
钩子函数
上传fileList数据构造
dom: this.$refs
选中文件 上传
按照整个上传的流程
fileList中每个对象的状态
- 刚放进去,准备好了待上传
- 上传中
- 上传完成
自己创建的一个文件对象
数据层fileList
弄一个数据同步v-model或.async,我就给你一个数据不希望它有什么同步的功能,我自己身上有一份数据,用户的数据也格式化放到这个数组里不涉及什么子改父父改子,自己处理自己的数据。
文件变化了,触发文件变化的钩子。
发起ajax上传请求
httpPost的处理
处理上传前+上传中+上传成功的各状态展示
file.status percent
onProgress onSuccess onError
upload.vue
<template>
<div class="zh-upload">
<div class="zh-upload-button" @click="upload">
<slot></slot>
</div>
<div><slot name="tip"></slot></div>
<input ref="file" type="file" :accept="accept" :multiple="multiple" @change="changeFile">
<ul>
<li v-for="file in files">
{{file.name}}
<zh-progress v-if="file.status==='uploading'" :percent="file.percent"></zh-progress>
</li>
</ul>
</div>
</template>
<script>
import _ from 'lodash'
import {ajax} from './upload'
export default {
name:'zh-upload',
props:{
name:{
type:String,
default:'file'
},
action:{
type:String,
default:''
},
accept:{
type:String,
default:''
},
multiple:{
type:Boolean,
default:false
},
limit:{
type:Number,
default:0
},
onExceed:{
type:Function,
},
beforeUpload:{
type:Function,
},
httpRequest:{
type:Function,
default:ajax,
},
fileList:{
type:Array,
default:[]
}
},
data(){
return {
files:[],
uniqueId:1,
}
},
watch:{
fileList:{
deep:true,
immediate:true,
handler(val){
this.files=val.map(item=>{
item.uid=`${+new Date}${this.uniqueId++}`
item.status='success'
return item;
// const file={
// uid:item.uid,
// name:item.name,
// url:item.url,
// status:'success', // 完成成功态时只关心 name & url
// percent:0,
// }
// return file;
})
}
}
},
methods:{
upload(){
this.$refs.file.value=''
this.$refs.file.click()
},
changeFile(ev){
let files=ev.target.files;
// 限制最多上传的文件数
if(this.limit && this.files.length+files.length>this.limit){
return this.onExceed();
}
// [...files].forEach
_.forEach(files,rawFile=>{
this.uploadStart(rawFile)
this.uploadFile(rawFile)
})
},
uploadStart(rawFile){
rawFile.uid=`${+new Date}${this.uniqueId++}`
// 构造新的文件对象
const fileNew={
uid:rawFile.uid,
name:rawFile.name,
size:rawFile.size,
type:rawFile.type,
status:'uploadstart',
percent:0,
rawFile,
}
this.files.push(fileNew)
},
uploadFile(rawFile){
// @todo beforeUpload
if(typeof this.beforeUpload === 'function'){
let flag=this.beforeUpload(rawFile) // 目前没考虑promise的情况
if(!flag) return
}
this.post(rawFile)
},
post(rawFile){
const options={
filename:this.name,
file:rawFile,
action:this.action,
onSuccess:(res)=>{
this.handleSuccess(res,rawFile)
},
onError:(res)=>{
},
onProgress:(ev)=>{
this.handleProgress(ev,rawFile)
},
}
this.httpRequest(options)
},
handleSuccess(res,rawFile){
const file=this.files.find(f=>f.uid===rawFile.uid)
file.status='success'
},
handleProgress(ev,rawFile){
// file是原生file文件,找到files中对应的file对象
const file=this.files.find(f=>f.uid===rawFile.uid)
file.status='uploading'
file.percent=Math.round(ev.loaded/ev.total*100)
}
}
}
</script>
<style scoped lang="scss">
.zh-upload{
&-button{
display: inline-block;
}
input[type=file]{
display: none;
}
}
</style>
upload.js
export function ajax(options){
let xhr=new XMLHttpRequest()
const {filename,file,action,onSuccess,onError,onProgress}=options;
const fd=new FormData
fd.append(filename,file)
xhr.open('post',action)
xhr.onload=()=>{
onSuccess(JSON.parse(xhr.responseText))
}
xhr.onerror=()=>{
onError(JSON.parse(xhr.errorText))
}
xhr.upload.onprogress=(ev)=>{
onProgress(ev)
}
xhr.send(fd)
return xhr;
}
progress.vue
<template>
<div class="progress-outer" :style="outerStyle">
<div class="progress-inner" :style="innerStyle"></div>
</div>
</template>
<script>
export default {
name:'zh-progress',
props:{
strokeWidth:{
type:Number,
default:10
},
strokeColor:{
type:String,
default:'blue'
},
percent:{
type:Number,
default:0
}
},
computed:{
outerStyle(){
return {
height:`${this.strokeWidth}px`,
}
},
innerStyle(){
return {
width:`${this.percent}%`,
background:this.strokeColor
}
}
},
watch:{
percent(val){
console.log(val,'percent');
}
}
}
</script>
<style scoped lang="scss">
.progress-outer{
width: 100%;
background: grey;
position: relative;
.progress-inner{
position: absolute;
left: 0;
top: 0;
height: 100%;
transition:width .3s ease;
}
}
.progress-outer,.progress-inner{
border-radius: 5px;
}
</style>
设计组件思想:
用户要有那些功能
暴露用户那些功能
用户有哪些行为
拖拽上传
主要就是onDrop事件
ondragover.prevent ondragleave.prevent
Popover组件
appendChild insertBefore都会对dom有移动性
事件:事件机制,谁在谁里面,怎么触发这个事件,事件都有哪些问题。
具体位置:用js算left top的值