前幾天時間給element-ui提了一個問題,結(jié)果沒有鳥我痪署,沒辦法狼犯,只能修改源碼來滿足需求了
本文討論的組件屬性僅限于
list-type='picture-card'
? 現(xiàn)在的問題是這樣的:
? element-ui中有一個upload組件悯森,可以上傳圖片或文件绪撵。該組件有很豐富的鉤子函數(shù)與配置幻碱,但是沒有一個限制上傳圖片數(shù)量(即使是按鈕禁用)的方法细溅,于是我就自己封裝了一下:
<el-upload :disabled='is_max' @s='...' @r='....' ...其余配置></el-upload>
export default = {
props:['num'], // 限制上傳數(shù)量
methods:{
on-success:function(r,f,fl){
this.is_max = true;
if(this.num && this.num === fl.length){
this.is_max = false;
}
this.$emit('s',f,fl);
},
on-remove:function(f,fl){
this.is_max = false;
this.$emit('r',f);
},
on-progress:function(){
this.is_max = true;
}
}
}
? 封裝添加了一個num參數(shù)恍风,限制上傳數(shù)量朋贬,并在上傳過程中禁用上傳按鈕兄世。
? 最初的目的是達(dá)到上傳數(shù)量限制御滩,就禁止上傳削解,但是可以刪除圖片,刪除后解除限制济似。
? 這個需求在1.3.7版本可以實現(xiàn),但是在后面的某一次(可見我上一篇文章的結(jié)尾)commit中被修改了,刪除與上傳同時受disabled影響潭流,如果設(shè)置disabled='true'灰嫉,那么這個組件變成了一個純展示作用讼撒,無法刪除根盒,無法添加贡这。
? 后來決定暫時用1.3.7版本打包,先出功能。
? 原以為事情可以緩一緩责掏,沒想到產(chǎn)品對那個上傳按鈕十分不滿换衬,認(rèn)為如果達(dá)到了上限,這個按鈕會產(chǎn)生困惑叫潦,應(yīng)該消失短蜕。禁用不行,里面弄個叉也不行没龙,必須要消失解滓!
? 好吧洼裤,只能來一發(fā)硬的,改源碼。
? 上傳按鈕控制的標(biāo)簽如下所示道伟,type為picture,text,picture-card之一。
<div class='el-upload el-upload--(type)'></div>
? 當(dāng)時有兩個想法:
1、修改disabled的默認(rèn)行為灰蛙,讓上傳按鈕消失,刪除按鈕不作用
2隔躲、引入新的變量摩梧,單獨控制上傳按鈕
? 由于不知道源碼的內(nèi)部行為,所以先想著蹭越。
? 一開始想法十分簡單障本,找到了目錄node_modules\element-ui\packages\upload\src下的upload.vue文件,為了看效果,直接添加了一行代碼:
// line-163
const data = {
class: {
'el-upload': true,
'abc': true, // 這里是我自己加的
},
on: {
click: handleClick
}
};
? 保存-打包-打開網(wǎng)頁-F12驾霜,發(fā)現(xiàn)并沒有abc這個類被加上去案训,查看打包后的JS文件,依然只有一個el-upload類粪糙。
? 這就十分尷尬了强霎,于是換了個地方,找到了node_modules\element-ui\lib下的upload.js文件蓉冈,添加了兩行代碼:
// line-934
var data = {
class: {
'el-upload': true,
'abc': true //同上
},
on: {
click: handleClick
}
};
// line-1312
var oClass = { 'el-upload': true, 'abc':true};
oClass['el-upload--' + listType] = true;
? 這里直接修改了打包后的render函數(shù)城舞,我想著這次應(yīng)該穩(wěn)了,結(jié)果打包后一看寞酿,還是不行家夺。
? 這就十分尷尬了,只能上網(wǎng)搜搜伐弹。一開始找到教程拉馋,教你修改哪個文件,然后實現(xiàn)功能惨好,然而跟我之前的步驟一樣煌茴,不可信。繼續(xù)搜索日川,終于找到了一位小哥:https://segmentfault.com/a/1190000010932321
? 懶得看網(wǎng)頁的可以找一個安靜的文件夾蔓腐,依次運行以下指令:
git clone https://github.com/ElemeFE/element.git
cd element
npm install
? 此時,element的元身會被下載下來龄句。接下來修改package文件夾中的源碼回论,運行npm run dist指令,該指令會在當(dāng)前目錄生成一個lib文件夾撒璧,用這個文件夾替換node_modules中的lib文件夾透葛,然后打包就可以了笨使!
? 事不宜遲卿樱,試試!
? 找到了element\packages\upload\src下的upload.vue硫椰,再次進(jìn)行修改:
class: {
'el-upload': true,
'abc':true,
},
? 又是熟悉的操作繁调,然而報了2個error,一臉懵逼的看錯誤信息:
1靶草、Missing space before value for key 'abc'
2蹄胰、Unexpected trailing comma
? 憑著我過硬的英文功底修正了錯誤:
1、冒號后面要加空格
2奕翔、最后一個屬性的逗號要省略
? 修改后再次運行裕寨,眼前閃過一片花花綠綠,成了。
? 覆蓋了lib文件夾后再次嘗試宾袜,可以驚喜的看到:
<div class='el-upload abc el-upload--picture-card'></div>
? 感覺人生都陽光起來了,接下來是實現(xiàn)目標(biāo)的時候了捻艳。
? 簡略的看了下upload組件內(nèi)部,有包含5個組件index庆猫、iframe-upload认轨、upload-dragger、upload-list月培、upload嘁字。
? 其中iframe-table是造一個表單,然后表單提交圖片信息杉畜,略過纪蜒。
? upload-dragger是拖拽功能,略過。
? upload-list實現(xiàn)已圖片上傳預(yù)覽妖枚,略過丸相。
? 需要關(guān)注的只有整合所有組件的index與上傳按鈕相關(guān)的upload組件了。
? index.vue的整個大概是這樣:
<div>
<upload-list></upload-list> <!-- 用于展示圖片 僅當(dāng)picture-card類型時在前面 -->
<uploadComponent> <!-- 上傳 內(nèi)部的DOM會被當(dāng)成上傳按鈕 通過FormDataAPI決定調(diào)用表單或者ajax -->
<upload></upload> <!-- drag屬性決定是否支持拖拽 -->
</uploadComponent>
</div>
? 仔細(xì)看了一遍內(nèi)部實現(xiàn)杆烁,再回頭看一眼之前的2個想法,決定用第二個简卧,修改默認(rèn)有點麻煩兔魂。
? 方式比較簡單粗暴,在所有定義diabled的地方加上了一條新語句举娩,比如:
// line-94
{
disabled: Boolean
Jimmy_input_btn_disabled: Boolean
}
? 這樣就自定義了一個新的數(shù)據(jù)析校,第二步,通過這個來阻止上傳按鈕生成铜涉。
? 找來找去智玻,只有一個地方可以控制:
// line-254
// 這里是上傳按鈕的渲染點
const uploadComponent =
this.Jimmy_input_btn_disabled ? '' : // 我加的
(typeof FormData !== 'undefined' || this.$isServer)
? <upload {...uploadData}>{trigger}</upload>
: <iframeUpload {...uploadData}>{trigger}</iframeUpload>;
? 加上后,打包測試:
<el-upload :Jimmy_input_btn_disabled='is_max' ...其余配置></el-upload>
? 在圖片達(dá)到上限后芙代,上傳按鈕驚喜的消失了吊奢,舒服!
? 然而纹烹,在下一秒页滚,我刪除圖片的時候,就報了一個錯铺呵,abort無法執(zhí)行裹驰。
? 順著報錯信息,找到了這里:
// 刪除時候會調(diào)用的函數(shù)
handleRemove(file, raw) {
if (raw) {
file = this.getFile(raw);
}
this.abort(file);
// ...
},
//...
abort(file) {
this.$refs['upload-inner'].abort(file);
},
? 這個upload-inner呢片挂,在index.vue也有定義:
const uploadData = {
props: {
// ...一堆參數(shù)
},
ref: 'upload-inner'
};
? 再看看上面uploadComponent的定義幻林,我瞬間明白了贞盯,這個ref被作為參數(shù)傳給了上傳按鈕,刪除圖片需要執(zhí)行綁定在該按鈕上面的abort函數(shù)沪饺。問題是邻悬,這個按鈕被我弄沒了,而且由于vue的'析構(gòu)'随闽,DOM上的事件也沒了父丰。
? 結(jié)果就是,不可行掘宪,粗暴是不對的蛾扇,兩個方案同時否決。
? 想了好久魏滚,既然不能讓DOM消失镀首,那么弄成display:none不就OK了么。
? 兩個方案:
1鼠次、根據(jù)某個條件動態(tài)渲染一個自定義的class更哄,該class定義為diaplay:none
2、根據(jù)某個條件動態(tài)渲染行內(nèi)樣式display:none
? 由于render函數(shù)不太記得用法腥寇,所以第二個看起來實現(xiàn)有點難度成翩,決定用第一個。當(dāng)然赦役,這個條件不能是默認(rèn)的diabled麻敌,所以,我的Jimmy_input_btn_disabled又可以出場了掂摔。
? 修改的時候遇到了瓶頸术羔,里面的代碼居然是JSX,我修改了upload.vue:
class: {
'el-upload': true,
'abc' : !!this.Jimmy_input_btn_disabled
},
? 很明顯乙漓,雖然數(shù)據(jù)傳進(jìn)來了级历,但是并沒有做到動態(tài)渲染,這里只是初始化叭披,所以abc沒有出現(xiàn)過寥殖,并且在變動的時候,根元素出現(xiàn)了這樣的情況:
<div Jimmy_input_btn_disabled = 'true'></div>
? 當(dāng)時真是笑尿我了趋观。
? 一度陷入了僵局扛禽,JSX不知道怎么寫锋边。如果是vue中的render函數(shù)皱坛,我還可以寫個demo然后模仿。
? 最后我甚至跑去看react的教程豆巨,但是人家變動數(shù)據(jù)用的是setState……
?
? 在不斷的嘗試中的剩辟,我是找到了辦法,簡單的吐血。
? 其實早就發(fā)現(xiàn)了贩猎,只是陷入了一個誤區(qū)熊户。
? 首先這個Jimmy_input_btn_disabled需要更改為String類型,作為一個動態(tài)類傳入:
disabled: Boolean,
Jimmy_inputbtn_disabled: String
? 第二步吭服,在JSX中直接寫class:
<div {...data} class={Jimmy_input_btn_disabled}>
...
</div>
? 源碼修改完成嚷堡,打包。
? html中如下:
<div :Jimmy_input_btn_disabled = 'xxx'></div>
? JS文件如下:
new Vue({
data:{
xxx:''
},
methods:{
s:function(){
// 達(dá)到上限
this.xxx = 'xxx';
},
r:function(){
// 解除
this.xxx = '';
}
}
});
? CSS很簡單艇棕,直接設(shè)置xxx{display:none}
就行了蝌戒。
? 最后測試結(jié)果十分成功,雙擊666沼琉!