最近不小心更新了element-ui的版本,已经到了2.1.0,以前修改的源码都失效了。

  于是重新尝试下面的指令重新修改:

git clone https://github.com/ElemeFE/element.git
cd element
npm install
npm run dist

  这时候会发现,不仅npm run dist的eslint日常报错,连npm install都报错了,不过是普通的operation not permitted,用管理员权限运行下cmd就OK了。

  问题在于这个dist出来的lib文件夹,我去掉了eslint的扫描指令后,可以成功打包,生成的文件夹覆盖后,有一个组件报错了:

  这真的是搞毛,我只是修改了upload的组件,分页组件居然报错了。

  我以为两者是关联的,但是我什么都不改,直接dist后,仍然会报这个错。

  这时只有两个选择,去修改pagination组件源码,尝试修复错误。或者不改源码,尝试从给的方法解决问题。

  因为如果改源码要锁定版本,还是非常坏事的,所以这次我决定从暴露出的API来解决我的问题。

  问题还是那个老的,upload组件点击删除图片时必须弹一个确认框,点击确认后才能删除,如图:

  这里我直接用了element-ui的另一个指令this.$message,之前是在源码里删除图片前直接加了一个判断:

// 根据变量控制流程
handleRemove(file, raw) {
// 添加的确认环节
if (this.jimmyRemoveTip) {
this.$confirm('此操作将永久删除图片, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// ...删除图片
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}else {
// 正常流程
}
}

  但是现在这条路行不通了,于是我尝试看看新版本的element-ui有没有加辅助代码。

  直接进入了element-ui的packages/upload/src/index.vue中,翻到handleRemove方法,惊喜的发现多了一个判断:

handleRemove(file, raw) {
if (raw) {
file = this.getFile(raw);
}
let doRemove = () => {
this.abort(file);
let fileList = this.uploadFiles;
fileList.splice(fileList.indexOf(file), 1);
this.onRemove(file, fileList);
}; if (!this.beforeRemove) {
doRemove();
} else if (typeof this.beforeRemove === 'function') {
const before = this.beforeRemove(file, this.uploadFiles);
if (before && before.then) {
before.then(() => {
doRemove();
}, noop);
} else if (before !== false) {
doRemove();
}
}
}

  这里有一个新的判断beforeRemove,跑去官网看,是这样解释的:

  这就很爽了,根据源码的情况和官网解释,只要返回一个false或者reject就能让删除操作停止。

  返回false是很简单,但是这里我只能用$message指令,这玩意返回的是一个Promise,于是我要想办法在点击取消的时候reject掉,一开始我是这样写的:

return this.$confirm('是否确定删除该图片 ?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(null, e => {
throw new Error('reject');
});

  相当暴力,这个当然解决了问题。但是抛出个错误总觉得很不雅,最后改成了这样:

return this.$confirm('是否确定删除该图片 ?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(null).catch(e=>{
e();
});

  

  解决了问题一,还剩另外一个。当上传的图片到上限时,需要把上传按钮隐藏。

  目前upload组件有一个关于上限的参数,是limit,但是这个参数很局限,在上传图片达到上限时,会中止上传,源码如下:

uploadFiles(files) {
// 达到上限调用on-exceed钩子函数并返回
if (this.limit && this.fileList.length + files.length > this.limit) {
this.onExceed && this.onExceed(files, this.fileList);
return;
} let postFiles = Array.prototype.slice.call(files);
if (!this.multiple) { postFiles = postFiles.slice(0, 1); } if (postFiles.length === 0) { return; } postFiles.forEach(rawFile => {
this.onStart(rawFile);
if (this.autoUpload) this.upload(rawFile);
});
}

  虽然可以在on-exceed钩子函数中提示用户图片上传达到上限,但是产品要上传按钮消失,这就很头疼了……

  目前还未找到完美的解决办法,先在钩子函数中做上限提示……

  在不久之前,后台也提了一个需求,说多张上传不能太多,最多一次10张,不然图片服务器处理不过来。

  如果能改源码,这个需求分分钟就搞定。但是现在行不通,只能从现存的API来想办法。

  整个组件结构很简单:

// 根据类型摆放图片展示list的位置
// uploadComponent就是那个上传按钮
// $slots.default是白框框中间自定义的图标
// $slots.tip是下面的文字
<div>
{ this.listType === 'picture-card' ? uploadList : ''}
{
this.$slots.trigger
? [uploadComponent, this.$slots.default]
: uploadComponent
}
{this.$slots.tip}
{ this.listType !== 'picture-card' ? uploadList : ''}
</div>

  示例代码如下图所示:

<div class="upload-content">
<el-upload
/*...*/>
<!-- slot默认为default -->
<p>default</p>
<p slot='tip'>tips</p>
</el-upload>
</div>

  效果图:

  而uploadList组件就是已上传的图片展示组件,这个组件没有暴露API,纯展示,所以我的第二个问题暂时不好处理。

  

  接下来看上传流程,整个upload组件的入口代码如下:

<input class="el-upload__input" type="file" ref="input" name={name} on-change={handleChange} multiple={multiple} accept={accept}></input>

  核心上传按钮,属性直接看就懂了,点击上传按钮后,监听原生on-change事件,触发handleChange:

handleChange(ev) {
// 获取事件对应的files
const files = ev.target.files; if (!files) return;
this.uploadFiles(files);
}

  我这个问题在一开始就可以得到解决在这个地方加一个判断就OK了,如下:

handleChange(ev) {
// 获取事件对应的files
const files = ev.target.files; if (!files) return;
// 假设有一个自定义参数concurrenceUploadNumber限制同时上传文件数
if (files.length > this.concurrenceUploadNumber) {
this.onConcurrenceUpload && this.onConcurrenceUpload(files, this.fileLise);
return;
}
this.uploadFiles(files);
}

  这不就解决了么……然而并没有这个东西。

  方法获取对应事件的文件或文件列表,调用uploadFiles方法:

uploadFiles(files) {
// 文件超过限制触发钩子函数并返回
if (this.limit && this.fileList.length + files.length > this.limit) {
this.onExceed && this.onExceed(files, this.fileList);
return;
}
// 转换为纯数组
let postFiles = Array.prototype.slice.call(files);
// 单文件上传模式只取第一个
if (!this.multiple) { postFiles = postFiles.slice(0, 1); } if (postFiles.length === 0) { return; }
// 调用forEach依次上传
postFiles.forEach(rawFile => {
this.onStart(rawFile);
// 根据参数进行自动上传
if (this.autoUpload) this.upload(rawFile);
});
}

  这里后台跟我吐槽为什么要一个一个上传,弄成一个请求多好,同时上传100张就要发100次请求,所以服务器处理不过来。

  因为上传这部分之前了解较少,所以也不明白这里为什么这样处理。

  先不看onStart方法,过一下自动上传流程:

upload(rawFile, file) {
// 清空上传按钮内容
this.$refs.input.value = null;
// 判断是否存在before-upload钩子函数
if (!this.beforeUpload) {
return this.post(rawFile);
}
// 钩子函数处理
const before = this.beforeUpload(rawFile);
if (before && before.then) {
before.then(processedFile => {
const fileType = Object.prototype.toString.call(processedFile);
if (fileType === '[object File]' || fileType === '[object Blob]') {
this.post(processedFile);
} else {
this.post(rawFile);
}
}, () => {
this.onRemove(null, rawFile);
});
} else if (before !== false) {
this.post(rawFile);
} else {
this.onRemove(null, rawFile);
}
}

  然后调用了post方法做文件上传准备:

post(rawFile) {
const { uid } = rawFile;
// 上传文件的参数与回调函数
const options = {
headers: this.headers,
withCredentials: this.withCredentials,
file: rawFile,
data: this.data,
filename: this.name,
action: this.action,
onProgress: e => {
this.onProgress(e, rawFile);
},
onSuccess: res => {
this.onSuccess(res, rawFile);
delete this.reqs[uid];
},
onError: err => {
this.onError(err, rawFile);
delete this.reqs[uid];
}
};
// 上传
const req = this.httpRequest(options);
this.reqs[uid] = req;
// 返回结果
if (req && req.then) {
req.then(options.onSuccess, options.onError);
}
}

  这里的上传方法httpRequest可以自定义,默认是内置的ajax方法。

  ajax就没什么营养了,所以正常人使用的上传流程大概就是这样。

  但是我不行啊,需要不修改源码的情况下完成特殊需求,还是有点……

  过了一遍流程,发现可操作的地方只有一个地方,那就是auto-upload那里,将自动上传置false,然后进行二次处理,所以之前的流程会断:

postFiles.forEach(rawFile => {
// 进入onStart
this.onStart(rawFile);
// 不进行上传
if (this.autoUpload) this.upload(rawFile);
})

  这里看一下onStart的内容:

// 'on-start': this.handleStart
handleStart(rawFile) {
// 随机数
rawFile.uid = Date.now() + this.tempIndex++;
// 包装文件对象
let file = {
status: 'ready',
name: rawFile.name,
size: rawFile.size,
percentage: 0,
uid: rawFile.uid,
raw: rawFile
};
// 转换URL以便展示
try {
file.url = URL.createObjectURL(rawFile);
} catch (err) {
console.error(err);
return;
}
// 将文件弹入图片展示数组
this.uploadFiles.push(file);
// 触发onChange事件
this.onChange(file, this.uploadFiles);
}

  这个函数主要是封装了file对象,展示上传的图片但不做上传操作。

  这样的话就有操作空间了,于是我监听了onChange事件并看看返回的参数内容:

<el-upload
/*...*/
:on-change='uploadFile'>
</el-upload>

  打印内容如下:

  其中第一个参数是封装的file对象,url是转换的本地路径以便展示。

  第二个参数是uploadList的组件参数,会将之前已有的图片也加进来。

  如果同时上传10张照片,会触发10次onChange事件,由于不会做上传操作,所以可以在前端处理掉。

  当时我想着不如在这里把所有上传的请求整合成一个,但是由于每个文件上传都会触发该事件,我又无法获取上传文件数量,所以想着还是限制吧。

  假想代码如下:

uploadFile(f, fl) {
let localFileList = this.list,
len = localFileList.length;
// 获取上传的图片数量
let uploadFileList = fl.slice(len);
if (uploadFileList.length > 10) {
// 不进行上传
} else {
// 正常上传
}
}

  然而这是不现实的,问题还是每个上传都会触发该事件,所以会重复上传之前的图片。

  不搞了……不想自己封装组件,木有UI天赋。

  没办法,已经提了Feature Request,不知道有没有用。

  

  啊……年后深圳求职,一年萌新求带走。

浅探element-ui2组件源码之upload的更多相关文章

  1. Element 2 组件源码剖析之布局容器

    0x00 简介 前文分析过组件的 布局栅格化(Grid Layout) ,通过基础的 24 分栏,迅速简便地创建布局. 本文将介绍用于布局的容器组件,使用 Flexbox 功能将其所控制区域设定为特定 ...

  2. element-ui input组件源码分析整理笔记(六)

    input 输入框组件 源码: <template> <div :class="[ type === 'textarea' ? 'el-textarea' : 'el-in ...

  3. .NET开发邮件发送功能的全面教程(含邮件组件源码)

    今天,给大家分享的是如何在.NET平台中开发“邮件发送”功能.在网上搜的到的各种资料一般都介绍的比较简单,那今天我想比较细的整理介绍下: 1)         邮件基础理论知识 2)         ...

  4. Django-restframework 源码之认证组件源码分析

    Django-restframework 源码之认证组件源码分析 一 前言 之前在 Django-restframework 的流程分析博客中,把最重要的关于认证.权限和频率的方法找到了.该方法是 A ...

  5. element-ui 组件源码分析整理笔记目录

    element-ui button组件 radio组件源码分析整理笔记(一) element-ui switch组件源码分析整理笔记(二) element-ui inputNumber.Card .B ...

  6. element-ui Message组件源码分析整理笔记(八)

    Message组件源码: main.js import Vue from 'vue'; import Main from './main.vue'; import { PopupManager } f ...

  7. element-ui Steps步骤条组件源码分析整理笔记(九)

    Steps步骤条组件源码: steps.vue <template> <!--设置 simple 可应用简洁风格,该条件下 align-center / description / ...

  8. rest_framework解析器组件源码流程

    rest_framework解析器组件源码流程 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数 ...

  9. Rest_Framework之认证、权限、频率组件源码剖析

    一:使用RestFramwork,定义一个视图 from rest_framework.viewsets import ModelViewSet class BookView(ModelViewSet ...

随机推荐

  1. MySQL索引 - 索引的类型

    索引的类型 B-Tree索引 B-Tree 索引 通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同. B-Tree 索引 能够加快访问数据的速度,存储引擎不再需要进行全表扫描来获取需 ...

  2. 深入一致性哈希(Consistent Hashing)算法原理,并附100行代码实现

    转自:https://my.oschina.net/yaohonv/blog/1610096 本文为实现分布式任务调度系统中用到的一些关键技术点分享——Consistent Hashing算法原理和J ...

  3. Sass和Compass入门

    一.前言 1.Sass是什么? Sass可以简化你的Css工作流,并可以使你的Css的扩展和维护工作变的更加容易!例如,曾几时何,因为客户的需求的变更,你必须不断的通过查找和替换来更改一个像素值,或者 ...

  4. JavaSE(四)之接口、访问控制

    上面我们学习了几个修饰符,在开发中经常会用的到,所以必须熟练的掌握.接下来我学习一下接口和访问控制. 一.接口 一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方 ...

  5. NOI2017&&codeM2017游记

    7.17 坐了一天动车到绍兴,宿舍环境什么的还是很棒的. 7.18 早上开幕式,没啥好看的,例行节目+讲话. 下午笔试,顺利满分,不过ccz挂了一道多选,99分,影响应该不是很大. 练习赛出人意料地没 ...

  6. 2017 Multi-University Training Contest - Team 1 1001&&HDU 6033 Add More Zero【签到题,数学,水】

    Add More Zero Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)T ...

  7. Java集合系列[4]----LinkedHashMap源码分析

    这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHas ...

  8. MongoDB模拟多文档事务操作

    Mongodb不支持多文档原子性操作,因此依据两阶段提交协议(Two Phase Commits protocol)来模拟事务. 以两个银行账户之间的转账行为为例,来说明如何实现多文档间的事务操作. ...

  9. Ubuntu下 jdk环境变量设置

    流程 1. 官网下载对应的jdk文件 2. 在根目录 / 下创建一个java目录 mkdir /java 3. 使用mv命令 将下载下来的文件(压缩格式),移动到上一步创建的/java目录下   Ps ...

  10. ECharts插件的使用

    ECharts插件:官网下载echarts.js开发者可以选择源码.下载地址:http://echarts.baidu.com/download.html 下载之后,echarts.js放在js文件夹 ...