5.3.1.快速使用
高级表格组件 EleProTable 基于 EleDataTable 和 EleVirtualTable 封装, 主要是对请求参数的封装,可以很方便的实现请求后端接口分页、排序、筛选查询数据展示, 以下是 EleAdminPlus 提供的几个表格组件,可以用在不同的场景中:
EleDataTable 组件是 v1.1.5 版本开始从 EleProTable 中抽取出来为单独组件,以方便某些场景下直接使用,使用示例:
运行代码 新窗口打开
5.3.2.属性列表
支持 ElTable 的全部属性(请查看 Element 文档),这里只列出额外增加的属性:
属性 rowClickChecked 设置为 true 即可在多选时点击行选中,设置为 smart 只有一条选中时点击行为单选效果(可点击复选框多选)。
属性 currentRowKey 从 v1.1.6 开始支持 v-model:currentRowKey 直接同步和修改单选选中比调用 setCurrentRow 方法更简单。
属性 selectedRowKeys 也可 v-model:selectedRowKeys 直接同步和修改多选选中比调用 toggleRowSelection 方法更简单。
注意如果使用 selectedRowKeys 属性,一定要设置 rowKey 属性配置数据中能代表数据唯一值的字段名
5.3.3.事件列表
支持 ElTable 的全部事件(请查看 Element 文档),这里只列出额外增加的事件:
这两个事件用于直接 v-model:currentRowKey 和 v-model:selectedRowKeys 同步表格的单选和多选。
5.3.4.插槽列表
注意 EleDataTable 没有默认插槽 default 了,因为表格的列是通过属性配置而不是写 ElTableColumn 组件。
5.3.5.实例方法
支持 ElTable 的全部实例方法(请查看 Element 文档),这里只列出额外增加的实例方法:
方法 updateSelectedAndChecked 的作用是当表格的数据 data 属性发生变化后调用, 因为单选和多选都有属性配置是否保留不存在的选中数据,所以当 data 改变后就会去检查剔除当前选中的数据, 为了提升性能没有对 data 做深度监听,所以当不是 data.value = [] 这种改变时需要手动调用这个方法。
5.3.6.表格列属性
表格列配置 columns 同样支持 ElTableColumn 组件的全部属性(请查看 Element 文档),此外增加的有:
5.3.7.格式化列内容
如果需要自定义列内容渲染像一些简单的处理比如日期时间列格式化可以直接使用 formatter 方法处理:
运行代码 新窗口打开
5.3.8.插槽自定义列内容
如果需要复杂的列渲染使用 slot 属性设置插槽名称然后使用插槽来自定义列内容:
<template #action="{ row }">
<el-link type="primary" :underline="false" @click="openEdit(row)">修改
<el-link type="danger" :underline="false" @click="remove(row)">删除
<template #nicknameHeader="{ column }">
const nickname = ref('');
const data = ref<User[]>([]);
const openEdit = (row: User) => {
console.log(row);
};
const remove = (row: User) => {
console.log(row);
};
const editStatus = (checked: boolean, row: User) => {
const status = checked ? 0 : 1;
row.status = status;
};
listUsers().then((list) => {
data.value = list;
});
运行代码 新窗口打开
5.3.9.动态显示表格列
如果需要动态控制表的的列,可以使用计算属性 computed 定义 columns 例如动态控制是否需要操作列:
运行代码 新窗口打开
5.3.10.实现展开行
同 ElTable 的实现方式,使用示例:
const data = ref<User[]>([]);
listUsers().then((list) => {
data.value = list;
});
运行代码 新窗口打开
实现展开子表格也是一样的用法,无非就是插槽里面再写一个表格组件。
5.3.11.添加表尾合计行
表尾合计行如果需要显示复杂的内容,summaryMethod方法可返回 VNode ,示例:
const data = ref([
{ pieceId: 1, title: '教学档案001', amount: 6 },
{ pieceId: 2, title: '教学档案002', amount: 9 }
]);
/** 表格合计行, Element文档虽然写的返回 string[] 实际返回 VNode 也是支持的 */
const getSummaries = ({ columns, data }) => {
const sums = []; // ts 版定义 VNode[] 类型会报错可以直接定义 any
columns.forEach((column, index) => {
if (column.property === 'amount') {
const sumAmount = data.map((item) => Number(item[column.property]))
.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
sums[index] = h('span', {
style: { color: sumAmount > 10 ? 'red' : 'green' }
}, String(sumAmount));
} else if (index === 0) {
sums[index] = h('span', {
style: { fontWeight: 'bold', color: 'blue' }
}, '合计');
}
});
return sums;
};
const changeData = () => {
data.value = [
{ pieceId: 3, title: '教学档案003', amount: 2 },
{ pieceId: 4, title: '教学档案004', amount: 6 }
]
};
</script>
运行代码 新窗口打开
5.3.12.自定义空数据图标
属性 emptyProps 可为 false 设置空数据时只显示文字不显示图标,也可以自定义空组件的属性:
<!-- 例如修改图标大小, 修改样式设置上下间距 -->
<template>
<ele-data-table
:empty-props="{
imageSize: 120,
style: { padding: '80px 0' }
}"
row-key="userId"
:columns="columns"
:data="data">
</ele-data-table>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import type { Columns } from 'ele-admin-plus/es/ele-data-table/types';
const columns = ref<Columns>([
{
label: '用户账号',
prop: 'username'
},
{
label: '用户名',
prop: 'nickname'
}
]);
const data = ref([]);
</script>
运行代码 新窗口打开
5.3.13.高度铺满
表格高度自适应铺满是指自动将窗口剩余的高度(减去搜索栏等的高度)都给表格,这个需求有用 js 来实现的,但无疑用 css 的 flex 实现性能更好:
<!-- 给表格设置 height="calc(100vh - 160px)" 也能实现, 但这种需要自己去思考应该减去多少 px -->
<!-- 而实现表格高度自适应铺满就是指的不需要自己去思考应该减多少 px -->
<template>
<div style="flex: 1; display: flex; flex-direction: column; overflow: auto;">
<div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
</div>
<ele-data-table
height="100%"
row-key="userId"
:columns="columns"
:data="data"
style="flex: 1"/>
<div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { listUsers } from '@/api/system/user';
const columns = ref([
{
label: '用户账号',
prop: 'username'
},
{
label: '用户名',
prop: 'nickname'
}
]);
const data = ref([]);
listUsers().then((list) => {
data.value = list;
});
</script>
运行代码 新窗口打开
5.3.14.拖拽排序
使用 sortablejs 实现拖拽排序功能,示例:
<template>
<div style="padding: 12px">
<!-- 增加 ref 以便获取组件节点 -->
<ele-data-table ref="tableRef" row-key="userId" :columns="columns" :data="data">
<template #handle>
<ele-text :icon="HolderOutlined" type="placeholder" class="sort-handle"/>
</template>
<template #status="{ row }">
<ele-dot v-if="row.status === 0" text="正常" size="8px"/>
<ele-dot v-else text="冻结" type="danger" :ripple="false" size="8px"/>
</template>
</ele-data-table>
<el-button @click="handleClick" style="margin-top: 16px">获取数据</el-button>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { HolderOutlined } from '@/components/icons';
import SortableJs from 'sortablejs';
const columns = ref([
{
columnKey: 'handle',
width: 58,
align: 'center',
slot: 'handle'
},
{
prop: 'username',
label: '用户账号',
minWidth: 110
},
{
prop: 'nickname',
label: '用户名',
minWidth: 110
},
{
columnKey: 'sex',
prop: 'sexName',
label: '性别',
width: 110,
align: 'center'
},
{
prop: 'createTime',
label: '创建时间',
width: 180,
align: 'center'
},
{
prop: 'status',
label: '状态',
width: 130,
align: 'center',
slot: 'status'
}
]);
const data = ref([
{
userId: 41,
username: 'user01',
nickname: '用户一',
status: 0,
createTime: '2020-09-15 17:49:37',
sexName: '女'
},
{
userId: 42,
username: 'user02',
nickname: '用户二',
status: 0,
createTime: '2020-09-15 17:50:20',
sexName: '男'
},
{
userId: 43,
username: 'user03',
nickname: '用户三',
status: 0,
createTime: '2020-09-15 17:50:51',
sexName: '女'
},
{
userId: 44,
username: 'user04',
nickname: '用户四',
status: 0,
createTime: '2020-09-15 17:51:50',
sexName: '男'
},
{
userId: 45,
username: 'user05',
nickname: '用户五',
status: 0,
createTime: '2020-09-15 17:52:22',
sexName: '女'
}
]);
const handleClick = () => {
console.log(data.value);
};
/** 表格组件 */
const tableRef = ref(null);
/** 排序实例 */
let sortableIns = null;
/** mounted 生命周期中创建 SortableJs 实例 */
onMounted(() => {
const tableEl = tableRef.value?.$el;
const tbodyEl = tableEl.querySelector(
'.el-table__inner-wrapper > .el-table__body-wrapper .el-table__body > tbody'
);
sortableIns = new SortableJs(tbodyEl, {
animation: 300,
draggable: '.el-table__row',
handle: '.sort-handle', // 拖拽手柄
onUpdate: ({ oldDraggableIndex, newDraggableIndex }) => {
// 拖拽后更新数据
if (typeof oldDraggableIndex === 'number' && typeof newDraggableIndex === 'number') {
const temp = [...data.value];
temp.splice(newDraggableIndex, 0, temp.splice(oldDraggableIndex, 1)[0]);
data.value = temp;
Array.from(tbodyEl.querySelectorAll('.el-table__row.hover-row')).forEach((el) => {
el.classList.remove('hover-row');
});
}
},
onChange: () => {
Array.from(tbodyEl.querySelectorAll('.el-table__row.hover-row')).forEach((el) => {
el.classList.remove('hover-row');
});
},
setData: () => {}
});
});
/** unmount 生命周期中销毁 SortableJs 实例 */
onBeforeUnmount(() => {
if (sortableIns) {
sortableIns.destroy();
sortableIns = null;
}
});
</script>
运行代码 新窗口打开
5.3.15.虚拟滚动
虚拟滚动表格组件 EleVirtualTable 基于 ElTableV2 封装,用法与 EleDataTable 一致,使用示例:
<template>
<ele-virtual-table
:height="360"
row-key="userId"
:columns="columns"
:data="data">
</ele-virtual-table>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import type { User } from '@/api/system/user/model';
import type { Columns } from 'ele-admin-plus/es/ele-data-table/types';
const columns = ref<Columns>([
{
label: '用户账号',
prop: 'username'
},
{
label: '用户名',
prop: 'nickname'
}
]);
const data = ref<User[]>([]);
data.value = Array.from({ length: 2000 }).map((_, i) => {
return {
userId: i + 1,
username: 'user-' + String(i + 1).padStart(6, '0'),
nickname: `用户${i + 1}`
};
});
</script>
运行代码 新窗口打开
需要注意的是虚拟滚动表格一定要设置 height 属性,否则就失去了虚拟滚动的意义,如果不设置默认为一行数据的高度。
EleVirtualTable 还额外提供了几个实例方法:
方法 updateTableData 的作用是为了提升性能没有对表格数据 data 做深度监听, 多以当不是 data.value = [] 这种改变时应该手动调用此方法更新表格数据。
方法 updateWrapSize 的作用是 ElTableV2 不支持自适应宽度,所以监听了容器宽度变化做自适应(ResizeObserver), 为了提升性能做了防抖处理,所以如果是主动改变容器尺寸是需要调用此方法以获得及时的自适应宽度。
5.3.16.静态表格
静态表格组件 EleTable 在样式上与 EleDataTable 保持一致,可用于打印、拖动排序等场景,使用示例:
<template>
<ele-table size="large" style="table-layout: fixed;">
<colgroup>
<col width="60px" />
<col />
<col width="100px" />
<col width="180px" />
</colgroup>
<thead>
<tr>
<th></th>
<th>项目名称</th>
<th>状态</th>
<th>进度</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in projectList" :key="row.id">
<td style="text-align: center">{{ index + 1 }}</td>
<td>{{ row.projectName }}</td>
<td>
<ele-text v-if="row.status === 0" type="success">进行中</ele-text>
<ele-text v-else-if="row.status === 1" type="danger">已延期</ele-text>
<ele-text v-else-if="row.status === 2" type="warning">未开始</ele-text>
</td>
<td>
<el-progress :percentage="row.progress" />
</td>
</tr>
</tbody>
</ele-table>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const projectList = ref([
{
id: '1',
projectName: '项目000000001',
status: 0,
progress: 30
},
{
id: '2',
projectName: '项目000000002',
status: 0,
progress: 10
},
{
id: '3',
projectName: '项目000000003',
status: 1,
progress: 60
}
]);
</script>
运行代码 新窗口打开
属性列表:
静态表格更多的是对样式的封装,可以自己基于静态表格封装自己的表格组件在一些业务场景下使用,以达到高级表格的某些效果,且性能会更好。
属性 hasHeader 和 hasFooter 只是对样式的优化,并不会决定 thead 和 tfoot 是否渲染,当写了 tfoot 时需要设置 hasFooter 为 true 。
5.3.17.静态表格固定列
静态表格要实现左右固定列效果也非常的简单,可以使用 css 的 sticky 定位实现:
<template>
<div style="overflow: auto;">
<ele-table style="min-width: 580px;table-layout: fixed;">
<colgroup>
<col width="60px" />
<col />
<col width="100px" />
<col width="180px" />
</colgroup>
<thead>
<tr>
<th style="position: sticky;left: 0;z-index: 98;">序号</th>
<th>项目名称</th>
<th>状态</th>
<th style="position: sticky;right: 0;z-index: 98;">进度</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in data" :key="row.id">
<td style="text-align: center;position: sticky;left: 0;z-index: 98;">{{ index + 1 }}</td>
<td>{{ row.projectName }}</td>
<td>{{ row.status }}</td>
<td style="position: sticky;right: 0;z-index: 98;">{{ row.progress }}</td>
</tr>
</tbody>
</ele-table>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const data = ref([
{
id: '1',
projectName: '项目000000001',
status: '进行中',
progress: '30%'
},
{
id: '2',
projectName: '项目000000002',
status: '已延期',
progress: '10%'
},
{
id: '3',
projectName: '项目000000003',
status: '未开始',
progress: '60%'
}
]);
</script>
运行代码 新窗口打开
5.3.18.静态表格固定表头
固定表头也可以使用 css 的 sticky 定位实现,还可以同时固定表头和固定列:
<template>
<div style="max-height: 128px;overflow: auto;">
<ele-table style="min-width: 580px;table-layout: fixed;">
<colgroup>
<col width="60px" />
<col />
<col width="100px" />
<col width="180px" />
</colgroup>
<thead style="position: sticky;top: 0;z-index: 99;">
<tr>
<th style="position: sticky;left: 0;z-index: 98;">序号</th>
<th>项目名称</th>
<th>状态</th>
<th style="position: sticky;right: 0;z-index: 98;">进度</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in data" :key="row.id">
<td style="text-align: center;position: sticky;left: 0;z-index: 98;">{{ index + 1 }}</td>
<td>{{ row.projectName }}</td>
<td>{{ row.status }}</td>
<td style="position: sticky;right: 0;z-index: 98;">{{ row.progress }}</td>
</tr>
</tbody>
</ele-table>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const data = ref([
{
id: '1',
projectName: '项目000000001',
status: '进行中',
progress: '30%'
},
{
id: '2',
projectName: '项目000000002',
status: '已延期',
progress: '10%'
},
{
id: '3',
projectName: '项目000000003',
status: '未开始',
progress: '60%'
}
]);
</script>
运行代码 新窗口打开
上面固定表头是给表格设置了最大高度实现的,如果不想设置表格的高度让 body 滚动的时候表头吸顶就不能同时设置固定列了,示例:
<template>
<div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<ele-table style="table-layout: fixed;">
<colgroup>
<col width="60px" />
<col />
<col width="100px" />
<col width="180px" />
</colgroup>
<thead style="position: sticky;top: 0;z-index: 99;">
<tr>
<th>序号</th>
<th>项目名称</th>
<th>状态</th>
<th>进度</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in data" :key="row.id">
<td style="text-align: center;">{{ index + 1 }}</td>
<td>{{ row.projectName }}</td>
<td>{{ row.status }}</td>
<td>{{ row.progress }}</td>
</tr>
</tbody>
</ele-table>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const data = ref([
{
id: '1',
projectName: '项目000000001',
status: '进行中',
progress: '30%'
},
{
id: '2',
projectName: '项目000000002',
status: '已延期',
progress: '10%'
},
{
id: '3',
projectName: '项目000000003',
status: '未开始',
progress: '60%'
}
]);
</script>
<!--
不能同时设置固定列是因为设置固定列的情况是表格宽度超出屏幕, 然后给表格上层的 div 加 overflow: auto ,
这样表头的 sticky 就也是相对于这个 div 了, 没法相对于 body 的滚动实现吸顶效果了,
因为 sticky 是相对于最近的一个 overflow 为可滚动的父元素
-->
运行代码 新窗口打开
这种不给表格设置高度也想同时吸顶表头和左右列可以将表头和主体分两个表格写,然后同步横向滚动条的滚动即可,示例:
<!-- 分两个表格还要给两个表格设置相同宽度和列宽, 否则列会对不齐 -->
<!-- 为了方便建议封装成一个组件, 这里新建一个 `sticky-table.vue` -->
<template>
<div>
<!-- 表头 -->
<div ref="headerRef" style="position: sticky;top: 0;z-index: 99;overflow: hidden;">
<ele-table :class="tableClass" :style="tableStyle">
<colgroup>
<slot name="colgroup"></slot>
</colgroup>
<thead>
<slot name="thead"></slot>
</thead>
</ele-table>
</div>
<!-- 主体 -->
<div style="overflow-x: auto;" @scroll="onScroll">
<ele-table :class="tableClass" :style="tableStyle">
<colgroup>
<slot name="colgroup"></slot>
</colgroup>
<tbody>
<slot name="tbody"></slot>
</tbody>
</ele-table>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
defineProps({
// 自定义表格样式
tableStyle: Object,
// 自定义表格类名
tableClass: String
});
// 表头
const headerRef = ref(null);
/* 滚动事件, 同步滚动表头 */
const onScroll = (e) => {
const el = headerRef.value;
const scrollLeft = e.currentTarget.scrollLeft;
if (el.scrollLeft != scrollLeft) {
el.scrollLeft = scrollLeft;
}
};
</script>
这里监听 scroll 事件同步表头和主体的横向滚动,插槽 colgroup 设置列宽,thead 设置表头,tbody 设置主体,使用示例:
<template>
<div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<sticky-table :table-style="{ minWidth: '580px', tableLayout: 'fixed' }">
<template #colgroup>
<col width="60px" />
<col />
<col width="100px" />
<col width="180px" />
</template>
<template #thead>
<tr>
<th style="text-align: center;position: sticky;left: 0;z-index: 98;">序号</th>
<th>项目名称</th>
<th>状态</th>
<th style="position: sticky;right: 0;z-index: 98;">进度</th>
</tr>
</template>
<template #tbody>
<tr v-for="(row, index) in data" :key="row.id">
<td style="text-align: center;position: sticky;left: 0;z-index: 98;">{{ index + 1 }}</td>
<td>{{ row.projectName }}</td>
<td>{{ row.status }}</td>
<td style="position: sticky;right: 0;z-index: 98;">{{ row.progress }}</td>
</tr>
</template>
</sticky-table>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
<div>其它内容</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
// 引入刚才封装的表格
import StickyTable from './sticky-table.vue';
const data = ref([
{
id: '1',
projectName: '项目000000001',
status: '进行中',
progress: '30%'
},
{
id: '2',
projectName: '项目000000002',
status: '已延期',
progress: '10%'
},
{
id: '3',
projectName: '项目000000003',
status: '未开始',
progress: '60%'
}
]);
</script>
运行代码 新窗口打开
5.3.19.表头工具栏
表头工具栏组件是用于高级表格的表头部分,当然此组件也可以单独使用,或与其它组件结合,使用示例:
<template>
<ele-toolbar title="我是标题">
<!-- 默认插槽添加左边内容 -->
<el-button type="primary">添加</el-button>
<el-button type="danger">删除</el-button>
<!-- tools 插槽添加右边内容 -->
<template #tools>
<ele-tool title="我是按钮" @click="onClick">
运行代码 新窗口打开
EleToolbar 的属性列表(无事件),主题 theme 属性设置为 default 会有背景色及边框:
EleTool 是工具栏中的图标按钮组件,支持的属性有(事件只有一个 click 事件):
5.3.20.树形表格
EleTreeTable 是一个以优雅的层级形式展示的树形表格组件(v1.3.0新增),使用示例:
运行代码 新窗口打开
大多数的树形表格组件都是以对某一列文本进行空格缩进的形式展现,EleTreeTable 是通过对序号列进行跨行合并来体现树的层级, 可以体现出树的包裹关系,对打印展示更加友好,尤其是在 BOM 相关的业务场景下非常实用,EleTreeTable 目前是用于 EleProTable 的打印, 和表单构建的数据编辑表格中,还未作为一个公开组件,所以目前的功能也比 ElTable 少很多,后面会持续完善到 ElTable 的所有功能。
属性列表:
插槽列表:
属性 columns 列配置的数据格式:
属性 data 表格数据必须要有 key 字段作为 v-for 循环的 key ,如果接口数据字段名不为 key 可以自己 map 处理数据修改。