基于Element UI封装的 u-table组件的使用
一、支持的功能
1.表格支持超链接
2.表格支持行内编辑 input输入框和select选择
3.支持tags
4.支持展开行
5.支持展开树形结构
6.支持排序
7.支持过滤器
8.宽度根据表头长度自适应(支持中英文),也可自己设置宽度
9.支持自定义(插槽),前端通过改变数据结构处理较好
二、后台传递的数据格式
1.columns 列的设置 (Array)
参数 | 是否必填 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|---|
type | 当前列的类型,非必填 | string | link/tags/edit | - | |
prop | 必填 | 列数据对应的字段名,当type==tags时 props是具体要显示的tags数组的字段名 | string | - | - |
label | 列名 | string | - | - | |
align | 对齐方式,默认居中 | string | left/center/right | center | |
width | 列宽,默认表头宽度 | Number | 表头宽度 | ||
filters | 数据过滤的选项,数组格式,数组中的元素需要有 text 和 value 属性。 | Array[{ text, value }] | - | - | |
filtersMethod | 数据过滤使用的方法,如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 true 就会显示。 | Function(value, row, column) | - | - | |
link | type=link时必填 | 跳转的链接 | string | - | - |
selectData | type=edit,如果当前编辑的是select时需要 | 当前编辑select的数据 | Object{},详情见下表 | - | - |
scopedSlots | 自定义插槽 | object | |||
formatter | 普通的html的自定义 | Function(row,column) | - | - | |
render | 包含element UI结构的自定义 | Function(h,ctx) h是h函数 | - | - | |
prop | type=tags时 | 见下方prop说明 |
selectData 的参数说明
参数 是否必填 说明 类型 可选值 默认值 label 必填 显示的名称 取的字段 string - - value 必填 具体的值取的字段 string - - list 必填 select选择器选择的数据 array - - 举例
selectData: { // 每一列选择的数据应该是一样的 label: 'name', // 值 名称 value: 'value', // 显示 名称list:[{ name: '类型1', value: '1'},{ name: '类型2', value: '2'},]}
type=tags 时 prop 参数说明
注意 :此时 prop是具体要显示的tags数组的字段名称(tags数组定义在listData(表格数据)里面
tags数据的参数说明如下
参数 是否必填 说明 类型 可选值 默认值 label 必填 tag文案 string - - type 类型 string success/info/warning/danger - effect 主题 string dark / light / plain light size 尺寸 string medium / small / mini mini 举例
listData中[ id:1, table_tags1: [ // 固定字段 { label:'失败', // tags的文案 必传 type: 'danger' // tags的样式 不必传递 可选值(空/success/info/warning/danger) }, { label: '成功', type: 'success' },]]columns中[{ type: 'tags', prop: 'table_tags1', // tag的名称 label: '状态1',},]
2.listData 表格数据 (Array)
参数 | 是否必填 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|---|
children | 当要展示树形结构时必填 | 控制树形结构,结构要和父元素结构一致 | |||
其他参数 |
3.options 表格的设置 (Object)
参数 | 是否必填 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|---|
stripe | 是否为斑马纹 table | boolean | - | false | |
loading | 是否添加表格loading加载动画 | boolean | - | - | |
mutiSelect | 是否支持列表项选中功能 | boolean | - | false | |
border | 是否显示表格竖线 | boolean | - | false | |
rowKey | 展开行信息和树形结构时必填,其他时候最好也带上 | 当前行唯一id的字段名称 | string | - | - |
expendRowRender | 是否展开行 | boolean | - | false |
expendRowRender 时前端需要插槽的形式传递给子组件
<u-tableref="tableRef":data="listData":columns="columns":options="options":operates="operates"@confirmEdit="confirmEdit" >// 插槽形式<template v-slot:expendRowRender="record">{{ record.row.description }}template>u-table>
三、前端调用
请求完接口拿到 listData columns options
<u-table ref="tableRef" :data="listData" :columns="columns" :options="options" :operates="operates" @confirmEdit="confirmEdit" > // 插槽形式显示行信息 <template v-slot:expendRowRender="record"> {{ record.row.description }} template>u-table>
<template> <div class="table"> <el-table id="iTable" ref="mutipleTable" v-loading.iTable="options.loading" :data="data" :stripe="options.stripe" :row-key="options.rowKey?options.rowKey:''" :border="options.border" :max-height="options.maxHeight || 500" @selection-change="handleSelectionChange" > <template v-if="options.emptySlot" slot="empty"> <slot :name="options.emptySlot" /> template> <el-table-column v-if="options.mutiSelect" type="selection" fixed="left" :reserve-selection="true" style="width: 55px;" /> <el-table-column v-if="options.expendRowRender" type="expand"> <template slot-scope="scope"> <slot name="expendRowRender" :row="scope.row" /> template> el-table-column> <template v-for="(column, index) in columns"> <el-table-column v-if="column.show!==false" :key="column.label" :prop="column.prop" :label="column.label" :align="column.align || 'center'" :min-width="fitColumnWidth(column.label,index)" :width="column.width" :sortable="column.sortable?column.sortable:false" :filters="column.filters" :filter-method="column.filtersMethod" :fixed="column.fixed" > <template v-slot:header="scope"> <slot v-if="column.scopedSlots && column.scopedSlots.customHeader" :name="column.scopedSlots.customHeader" :column="column" /> <template v-else> {{ column.label }} template> template> <template slot-scope="scope"> <template v-if="column.render"> <expand-dom :column="column" :row="scope.row" :render="column.render" :index="index" /> template> <template v-if="column.scopedSlots && column.scopedSlots.customRender"> <slot :name="column.scopedSlots.customRender" :row="scope.row" :column="column" /> template> <template v-else> <template v-if="column.formatter"> <span v-html="column.formatter(scope.row, column)" /> template> <template v-else-if="column.type==='link' && scope.row[column.link]"> <el-link type="primary" :href="scope.row[column.link]" :underline="false" target="_blank">{{ scope.row[column.prop] }}el-link> template> <template v-else-if="column.type==='tags' && scope.row[column.prop].length"> <el-tag v-for="item in scope.row[column.prop]" :key="item.label" :type="item.type" :effect="item.effect || 'light'" size="mini" style="margin: 2px;" > {{ item.label }} el-tag> template> <template v-else-if="column.type==='edit' && !column.scopedSlots"> <template v-if="column.selectData"> <el-select v-if="scope.row['table_edit']" v-model="scope.row[column.prop]" placeholder="请选择"> <el-option v-for="item in column.selectData.list" :key="item[column.selectData.value]" :label="item[column.selectData.label]" :value="item[column.selectData.value]" /> el-select> <span v-else>{{ scope.row[column.prop] }}span> template> <template v-else-if="column.datePicker"> <el-date-picker v-if="scope.row['table_edit']" v-model="scope.row[column.prop]" type="date" :placeholder="$t('form.selectPlaceholder')" style="width: 100%;" value-format="yyyy-MM-dd" /> <span v-else>{{ scope.row[column.prop] }}span> template> <template v-else> <el-input v-if="scope.row['table_edit']" v-model="scope.row[column.prop]" class="edit-input" /> <span v-else>{{ scope.row[column.prop] }}span> template> template> <template v-else> <span>{{ scope.row[column.prop] || scope.row[column.prop]===0 ? scope.row[column.prop]: '-' }}span> template> template> template> el-table-column> template> <template v-if="operates"> <el-table-column v-if="operates.list && operates.list.filter(_x => _x.show === true).length > 0" ref="fixedColumn" :label="$t('table.operate')" align="center" :width="operates.width" :fixed="operates.fixed" > <template slot-scope="scope"> <div class="operate-group"> <template v-for="(btn, key) in operates.list"> <template v-if="btn.show"> <template v-if="btn.inlineEdit"> <template v-if="scope.row['table_edit']"> <el-button :type="btn.type" size="mini" :icon="btn.icon" :disabled="btn.disabled || false" :loading="editLoading" @click="confirmEdit(key, scope.row)" >{{ '保存' }} el-button> <el-button :type="btn.type" size="mini" :icon="btn.icon" :disabled="btn.disabled || false" :plain="btn.plain" :loading="editLoading" @click="cancelEdit(scope.row)" >{{ '取消' }} el-button> template> <el-button v-else :type="btn.type" size="mini" :icon="btn.icon" :disabled="btn.disabled || false" :plain="btn.plain" @click="onEdit(key, scope.row)" >{{ btn.label }} el-button> template> <el-button v-else :key="btn.id" :type="btn.type" size="mini" :icon="btn.icon" :disabled="btn.disabled || false" :plain="btn.plain" @click.native.prevent="btn.method(key, scope.row)" >{{ btn.label }} el-button> template> template> div> template> el-table-column> template> el-table> div>template><script>// getMatchObj是根据 key value获取对象数组中的某个对象import { getMatchObj } from '@/utils/index'export default { name: 'UTable', components: { }, // 组件 components: { expandDom: { functional: true, props: { row: Object, render: Function, index: Number, column: { type: Object, default: null } }, render: (h, ctx) => { const params = { row: ctx.props.row, index: ctx.props.index } if (ctx.props.column) params.column = ctx.props.column return ctx.props.render(h, params) } } }, props: { // 表格数据 data: { type: Array, default: () => { return [] } }, // 表头数据 需要展示的列 /** * type: 当前列的类型 * prop:列数据对应的字段名 * label:列名 * align:对齐方式 默认居中 * width:列宽 默认 表头的宽度 * sortable: 是否排序 默认 false * * */ columns: { type: Array, default: () => { return [] } }, // 操作按钮组 === label: 文本,type :类型(primary / success / warning / danger / info / text),show:是否显示,icon:按钮图标,plain:是否朴素按钮,disabled:是否禁用,method:回调方法 inlineEdit: 是否是行内编辑 operates: { type: Object, default: () => { return { width: 'auto', type: 'text', plain: false, fixed: false, list: [] } } }, /** * * table 表格的控制参数 * mutiSelect 是否多选 * rowKey: 行数据的row-key * border: 是否显示表格竖线 * expendRowRender: 是否展开行 * emptySlot: 自定义空状态的插槽 * maxHeight: 流体高度 */ options: { type: Object, default: () => { return { stripe: false, // 是否为斑马纹 table // loading: true, // 是否添加表格loading加载动画 // highlightCurrentRow: false, // 是否支持当前行高亮显示 mutiSelect: false, // 是否支持列表项选中功能 border: false, // 是否显示表格竖线 expendRowRender: false // 是否展开行 } } } // 当有type ===edit时候 confirmEdit方法必须传递(前端做的事情) }, // 数据 data() { return { pageIndex: 1, multipleSelection: [], // 多行选中 currentPage4: 4, isEditing: false, // 控制操作按钮 editableArr: [], // 可编辑列表的字段名 editLoading: false // dataList:[] } }, computed: { }, watch: { // data:{ // handler(newValue,oldValue){ // if(newValue !== oldValue){ // this.dataList = this.data // } // }, // } }, created() { }, mounted() { this.getInit() }, methods: { getMatchObj, // 多行选中 handleSelectionChange(val) { this.multipleSelection = val this.$emit('handleSelectionChange', val) }, // 显示 表格操作弹窗 showActionTableDialog() { this.$emit('handelAction') }, handleSizeChange(val) { // console.log(`每页 ${val} 条`); }, handleCurrentChange(val) { // console.log(`当前页: ${val}`); }, fitColumnWidth(tableHead, index) { // 根据表头自适应列宽 let flexWidth = 0 for (const char of tableHead) { if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) { // 如果是英文字符,为字符分配8个单位宽度 flexWidth += 9 } else if (char >= '\u4e00' && char <= '\u9fa5') { // 如果是中文字符,为字符分配15个单位宽度 flexWidth += 18 } else { // 其他种类字符,为字符分配8个单位宽度 flexWidth += 8 } // if(index===0){ // flexWidth += 8 // } } flexWidth += 10 // 加上padding值 if (flexWidth < 60) { // 设置最小宽度 flexWidth = 60 } // if (flexWidth > 250) { // // 设置最大宽度 // flexWidth = 250 // } return flexWidth + 'px' }, getInit() { this.columns.map(v => { if (v.type === 'edit') { this.editableArr.push(v.prop) } }) }, // 行内编辑 onEdit(index, row) { this.isEditing = true this.$set(row, 'table_edit', true) // row.table_edit = true // 控制当前行 this.editableArr.map(v => { row['origin-' + v] = row[v] }) }, // 保存编辑 confirmEdit(index, row) { // this.isEditing = false // row.table_edit = false; // 删掉不必要的元素 // this.editableArr.map(v=>{ // delete row['origin-'+v] // }) // delete row.table_edit this.editLoading = true this.$emit('confirmEdit', { row: row, successFun: () => { this.editLoading = false this.isEditing = false row.table_edit = false }, errFun: () => { this.editLoading = false this.editableArr.map(v => { row[v] = row['origin-' + v] }) } }) }, // 取消编辑 cancelEdit(row) { this.isEditing = false row.table_edit = false this.editableArr.map(v => { row[v] = row['origin-' + v] }) // row.title = row.originalTitle } }}script><style lang="scss"> .el-pagination{ text-align: center; margin-top: 20px; }style>
具体封装代码如下
参数 | 是否必传 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|---|
data | 是 | 数据 | array | - | - |
columns | 是 | 各个列 | array | - | - |
options | 否 | 表格公共控制参数,详情见上方后台传递的数据格式 | object | - | - |
operates | 否 | 操作项 | - | - | |
confirmEdit | type=edit时必填 | 行内编辑时 ,保存的方法 | Function({ value, successFun, errFun}) | - | - |
handleSelectionChange | 多行选中后执行的方法 | Function(val) | - | - |