核心思路:监控整个上传的流程
上传成功 上传失败
类型:拖拽 多个文件上传
跟上传强关联的属性,上传必备的字段
name: 提交的那个formData字段名
action:ajax接口路径
limit:限制提交个数

钩子函数

dom: this.$refs
选中文件 上传
按照整个上传的流程
fileList中每个对象的状态
自己创建的一个文件对象
数据层fileList
弄一个数据同步v-model或.async,我就给你一个数据不希望它有什么同步的功能,我自己身上有一份数据,用户的数据也格式化放到这个数组里不涉及什么子改父父改子,自己处理自己的数据。
文件变化了,触发文件变化的钩子。





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


appendChild insertBefore都会对dom有移动性

事件:事件机制,谁在谁里面,怎么触发这个事件,事件都有哪些问题。
具体位置:用js算left top的值