項目作的愈來愈多,重複的東西不斷的封裝成了組件,慢慢的,頁面就組件化了,只須要定義組件配置數據,使用就行了,這是一件很是舒服的事情,這篇文章主要和你們講講如何對element-ui中的el-table進行二次封裝。vue
公有組件,能夠被任何頁面調用,首先要保證組件不和外部業務發生耦合,其次就是要設計合理的字段,使用時經過不一樣的配置便可使用。
那先大體來分析如下可能有的需求:git
目前封裝的組件並不算完美,不可能知足因此需求,這裏的話主要仍是和你們分享思路github
實現動態表頭,這個應該是許多使用table的朋友們的痛點,明明是同樣的東西,卻要寫多個表格,實在不能忍,讓咱們今天來一舉殲滅它。element-ui
el-table表頭有兩個必須的屬性,prop值和label名字,其餘非必須的有fixed,align,width或者min-width等,那由此可設計出一個這樣的數據結構:api
{ prop: 'name', label: '名稱', fixed: true/false, align: 'center', minWidth: 160 }
上面咱們得出了普通表頭列的設計,那咱們繼續分析,看看嵌套表格配置多了哪些字段。
根據element-ui官網文檔,能夠看到前面字段基本同樣,嵌套表格多了children字段,用來循環子級表頭,那由此咱們能夠設計出這樣的數據結構:數據結構
{ prop: 'name', label: '名稱', fixed: true/false, align: 'center', minWidth: 160, children: [ { prop: 'oldName', label: '舊名稱', fixed: true/false, align: 'center', minWidth: 160, }, { prop: 'newName', label: '新名稱', fixed: true/false, align: 'center', minWidth: 160, } ] }
表頭設計思路大概是這樣,並不複雜,根據業務需求,你們均可以設計適合本身使用的字段。
完整的表頭設計字段應該大概會是這個樣子這個是我的字段配置的例子,其中將prop字段改爲了value, 下面代碼統一會使用value代替prop。dom
fieldList: [ { label: '帳號', value: 'account' }, { label: '用戶名', value: 'name' }, { label: '所屬角色', value: 'role_name', minWidth: 120 }, { label: '性別', value: 'sex', width: 80, list: 'sexList' }, { label: '帳號類型', value: 'type', width: 100, list: 'accountTypeList' }, { label: '狀態', value: 'status', width: 90, type: 'slot', list: 'statusList' }, { label: '建立人', value: 'create_user_name' }, { label: '建立時間', value: 'create_time', minWidth: 180 }, { label: '更新人', value: 'update_user_name' }, { label: '更新時間', value: 'update_time', minWidth: 180 } ]
表頭設計只是將一些基本的需求實現了,可是實際業務每每更爲複雜,好比當前列要顯示的是圖片,tag,超連接,或者列的數據是一個id要顯示對應的label。組件化
以前定義的字段列表都是簡單的文字顯示,當有了不一樣的類型顯示需求,則意味着須要一個類型字段,type,根據業務需求,能夠設計知足image,tag,href等。
字段設計爲type爲image時,同時能夠考慮設計width和height字段。
字段設計爲href時,能夠同時設計顏色,跳轉方式字段。
好比:ui
{label: '設備信息', prop: 'deviceInfo', type: 'href', herf: 'https://www.baidu.com', target: '_blank'}, {label: '設備圖標', prop: 'deviceImage', type: 'image', src: 'https://www.baidu.com', height: '60px', width: 'auto'}
當列的數據是一個id的時候須要顯示對應的label,狀況又稍微複雜了一點,多種實現方法:this
講講第二種方式,將對應的列表傳入組件中,在組件內部進行轉換,須要設置當前字段的類型爲id轉換爲label的類型,我在字段上定義的是type: select,而後要定義相關的list,字段設計大概長這樣:
{ label: '菜單組件', value: 'component', type: 'select', list: 'componentList1' }
個人實現方式是定義了一個listType對象,而後把頁面上用到的list都掛在了這個對象上面,將listType傳入到table組件中,經過listType[item.list]能夠獲取到字段對應列表而後獲取對應的label顯示。
很是很是很是重要的slot,特別提醒你們,若是想寫複雜的組件,考慮到自定義類型,請必定去了解slot不瞭解的請戳
vue2.6+已經廢棄slot-scope官網api描述
父級在使用組件的時候,在組件標籤內編寫內容,將會組件內部<solt><slot/>接收到
父級設置傳入的插槽的名字,組件內部匹配到名字相同的插槽進行渲染。
組件內部具名插槽傳輸數據到父級(dom接收方,數據傳出方):
<!-- solt 自定義列--> <template v-if="item.type === 'slot'"> <slot :name="'col-' + item.value" :row="scope.row" /> </template>
父級獲取插槽數據渲染dom(dom傳出方,數據接收方):
<!-- 自定義插槽顯示狀態 --> <template v-slot:col-status="scope"> <i :class="scope.row.status === 1 ? 'el-icon-check' : 'el-icon-close'" :style="{color: scope.row.status === 1 ? '#67c23a' : '#f56c6c', fontSize: '20px'}" /> </template>
slot是自定義組件的神器。
回到table組件,咱們須要自定義顯示內容,設計的字段應該以下:
{ label: '菜單圖標', value: 'icon', type: 'slot' }
上面說的都是顯示字段設計的東西,如今開始分析表格的數據,從哪裏來,到哪裏去。
若是要偷懶,那麼必定是要把懶偷到底的,有一丁點多餘的工做要作,都是偷懶不成功的。
須要什麼:
定義一個api字段,將須要請求的接口傳入到組件中,若是有相關參數,須要同時將參數傳入到組件中
定義一個resFieldList,好比數據在res.content.data上,則傳入數據:
resFieldList: ['content', ‘data’] // 數據所在字段
組件內部則須要在接口請求成功以後作這樣一步操做:
let resData = res const resFieldList = tableInfo.resFieldList // 獲得定義的響應成功的數據字段 for (let i = 0; i < resFieldList.length; i++) { resData = resData[resFieldList[i]] }
數據獲取成功以後,建議使用父子組件雙向通訊,.sync或者自定義model均可以實現,將數據派發到父組件,而後由父組件傳入子組件渲染組件。
直接由組件內部獲取數據而且渲染可能會須要擴展等問題限制組件的使用範圍。
定義一個refresh字段,刷新頁面只須要設置爲:
// 刷新表格 tableInfo.refresh = Math.random()
而組件內部watch字段change,從新調獲取數據的接口,便可實現刷新功能
分析有哪幾種類型的事件:
不一樣的業務可能涉及到各類類型的事件,若是封裝成爲了組件,怎麼處理???
換一個思路,咱們把事件看做是一個類型操做,好比點擊是click,刪除是delete,那咱們只須要一個事件轉發器,好比:
// 數據渲染事件的派發 this.$emit('handleEvent', 'list', arr) // 表格選擇事件的派發 this.$emit('handleEvent', 'tableCheck', rows) // 點擊事件的派發 this.$emit('handleClick', event, data)
咱們定義事件中間件,組件內部發生事件時將事件的類型還有相關的數據派發,父級接收而且處理。
// 表格相關 tableInfo: { refresh: 1, initCurpage: 1, data: [], fieldList: [ { label: '帳號', value: 'account' }, { label: '用戶名', value: 'name' }, { label: '所屬角色', value: 'role_name', minWidth: 120 }, { label: '性別', value: 'sex', width: 80, list: 'sexList' }, { label: '帳號類型', value: 'type', width: 100, list: 'accountTypeList' }, { label: '狀態', value: 'status', width: 90, type: 'slot', list: 'statusList' }, { label: '建立人', value: 'create_user_name' }, { label: '建立時間', value: 'create_time', minWidth: 180 }, { label: '更新人', value: 'update_user_name' }, { label: '更新時間', value: 'update_time', minWidth: 180 } ], handle: { fixed: 'right', label: '操做', width: '280', btList: [ { label: '啓用', type: 'success', icon: 'el-icon-albb-process', event: 'status', loading: 'statusLoading', show: false, slot: true }, { label: '編輯', type: '', icon: 'el-icon-edit', event: 'update', show: false }, { label: '刪除', type: 'danger', icon: 'el-icon-delete', event: 'delete', show: false } ] } }
<!-- 表格 --> <page-table :refresh="tableInfo.refresh" :init-curpage="tableInfo.initCurpage" :data.sync="tableInfo.data" :api="getListApi" :query="filterInfo.query" :field-list="tableInfo.fieldList" :list-type-info="listTypeInfo" :handle="tableInfo.handle" @handleClick="handleClick" @handleEvent="handleEvent" > <!-- 自定義插槽顯示狀態 --> <template v-slot:col-status="scope"> <i :class="scope.row.status === 1 ? 'el-icon-check' : 'el-icon-close'" :style="{color: scope.row.status === 1 ? '#67c23a' : '#f56c6c', fontSize: '20px'}" /> </template> <!-- 自定義插槽狀態按鈕 --> <template v-slot:bt-status="scope"> <el-button v-if="scope.data.item.show && (!scope.data.item.ifRender || scope.data.item.ifRender(scope.data.row))" v-waves size="mini" :type="scope.data.row.status - 1 >= 0 ? 'danger' : 'success'" :icon="scope.data.item.icon" :disabled="scope.data.item.disabled" :loading="scope.data.row[scope.data.item.loading]" @click="handleClick(scope.data.item.event, scope.data.row)" > {{ scope.data.row.status - 1 >= 0 ? '停用' : '啓用' }} </el-button> </template> </page-table>