项目作的愈来愈多,重复的东西不断的封装成了组件,慢慢的,页面就组件化了,只须要定义组件配置数据,使用就行了,这是一件很是舒服的事情,这篇文章主要和你们讲讲如何对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>