elementplus、vxe、surely 三种表格组件的封装
elementplus、vxe、surely 三种表格组件的封装
目录
1. element-plus
1.1 功能描述
表头:
- 左侧操作按钮 —— 设置表格密度、设置表格全屏
- 右侧操作按钮 —— 针对 表格多行数据 进行操作
- 可以添加表格标题
- 可以调整 表头行 对齐方式
- 可以筛选
表身:
- 表格操作列 —— 针对 表格单行数据 进行操作
- 可以调整 表身行 对齐方式
- 可以自定义单元格样式

1.2 优缺点
- 优点:较为常见的一种表格,配置相对简单
- 缺点:数据量大时非常卡顿
1.3 全屏按钮组件
可以通过以下方式控制表格全屏状态:
- ESC 按键
- 点击切换全屏按钮
<!--
* @Description: 表格全屏按钮
* @Author: lyrelion
* @Date: 2022-04-12 10:41:17
* @LastEditors: lyrelion
* @LastEditTime: 2022-09-29 09:11:12
-->
<template>
<el-tooltip
effect="dark"
:content="fullScreen ? '取消全屏' : '全屏'"
placement="top"
:auto-close="1000"
>
<span class="tooltip-outline-none" @click="handleSetFullScreen()">
<el-icon v-if="!fullScreen" class="pointer tooltip-icon" :size="18">
<full-screen />
</el-icon>
<i v-if="fullScreen" class="fa fa-compress pointer tooltip-icon"></i>
</span>
</el-tooltip>
</template>
<script lang="ts">
import {
reactive,
toRefs,
defineComponent,
onMounted,
onUnmounted,
} from 'vue';
export default defineComponent({
name: 'TFullScreenButton',
emits: ['full-screen-change'],
setup(props, { emit }) {
// 响应式数据
const state = reactive({
// 是否全屏
fullScreen: false,
});
/**
* esc 按键事件
*/
const keydownEvent = (event: KeyboardEvent) => {
if (document.fullscreenElement) {
document.exitFullscreen();
}
if (event.keyCode === 27) {
state.fullScreen = false;
}
// 发送事件,切换全屏状态
emit('full-screen-change', state.fullScreen);
};
/**
* esc 窗口尺寸变化事件
*/
const windowResizeEvent = () => {
state.fullScreen = document.fullscreen;
// 发送事件,切换全屏状态
emit('full-screen-change', state.fullScreen);
};
/**
* 设置控制全屏显示状态值
*/
const handleSetFullScreen = () => {
// 设置状态值
state.fullScreen = !state.fullScreen;
// 浏览器全屏模式
if (state.fullScreen && document.body.requestFullscreen) {
document.body.requestFullscreen();
} else if (document.fullscreenElement) {
// 浏览器退出全屏模式
document.exitFullscreen();
}
// 发送事件,切换全屏状态
emit('full-screen-change', state.fullScreen);
};
onMounted(async () => {
// 监听 esc 按键事件 退出全屏
window.addEventListener('keyup', keydownEvent, false);
// 监听窗口尺寸改变事件 退出全屏
window.addEventListener('resize', windowResizeEvent, false);
});
onUnmounted(() => {
// 移除监听事件
window.removeEventListener('keyup', keydownEvent);
window.removeEventListener('resize', windowResizeEvent);
});
return {
...toRefs(state),
handleSetFullScreen,
};
},
});
</script>
1.4 表格行密度组件
使用 el-dropdown 组装出一个下拉框
用户选中紧凑程度后,组件抛出用户所选信息
表格组件会接收此组件抛出的密度信息
<!--
* @Description: 表格密度设置按钮
* @Author: lyrelion
* @Date: 2022-04-12 10:18:34
* @LastEditors: lyrelion
* @LastEditTime: 2022-04-12 10:40:56
-->
<template>
<el-dropdown trigger="click" @command="setTableDensity">
<el-tooltip content="密度" effect="dark" placement="top">
<el-icon class="pointer tooltip-icon" :size="18">
<operation />
</el-icon>
</el-tooltip>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in [
{ label: '正常', value: 'small' },
{ label: '紧凑', value: 'mini' },
{ label: '宽松', value: 'medium' },
]"
:key="item.label"
:class="{ active: tableSize === item.value }"
:command="item.value"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent } from 'vue';
export default defineComponent({
name: 'TDensityButton',
emits: ['table-size-change'],
setup(props, { emit }) {
// 响应式数据
const state = reactive({
// 表格尺寸
tableSize: 'mini',
});
/**
* 设置表格尺寸(密度)
* @param command 表格尺寸(密度)
*/
const setTableDensity = (command: string) => {
state.tableSize = command;
// 发送事件,告诉父组件表单尺寸改变了
emit('table-size-change', state.tableSize);
};
return {
...toRefs(state),
setTableDensity,
};
},
});
</script>
1.5 ElementPlus 表格组件
1.5.1 表格组件接收的数据 props
表头配置项数据类型
// 表格表头配置项
interface TableHeaderOptions {
prop?: string; // 当前列对应键名
label: string; // 当前列对应表头
width?: string | number; // 当前列对应宽度
minWidth?: string | number; // 对应列的最小宽度
align?: string; // 当前列数据对齐方式
headerAlign?: string; // 当前列表头对齐方式
fixed?: true | 'left' | 'right' | ''; // 当前列固定方式
sortable?: string | boolean | undefined; // 排序方式
slot?: string | undefined; // 插槽
type?: string; // 表格类型
action?: boolean; // 是否为操作列
}
具体接收的 props 如下所示
还写了 inheritAttrs: true, 用于继承 elementPlus 组件本身就能接收的属性
<!-- <span>表格组件接收到的 $attrs: {{ $attrs }}</span> -->
inheritAttrs: true,
props: {
// 表格数据
tableData: {
type: Array,
required: true,
default: () => [],
},
// 表头配置项
tableHeaderOpitons: {
type: Array as PropType<TableHeaderOptions[]>,
required: true,
default: () => [],
},
// 判断是否禁用多选方法(返回true不禁用)
selectable: {
type: Function,
default: (row: any, index: number) => true,
},
// 页码
pageNum: {
type: Number,
default: 1,
},
// 每页条数
pageSize: {
type: Number,
default: 10,
},
// 是否开启序号列(开启的话,必须传入每页条数/页码)
openIndexColumn: {
type: Boolean,
default: false,
},
// 是否开启选择列
openSelectColumn: {
type: Boolean,
default: false,
},
// 要修改背景的列 index 数组
changeBgColumnIndex: {
type: Array,
default: () => [],
},
// 要修改的背景色
changeBgColumnColor: {
type: String,
default: '#F5F7FA',
},
// 操作列是否显示
showOperationColumn: {
type: Boolean,
default: true,
},
// 表格顶部左侧列显隐控制
showTableLeftBtn: {
type: Object,
default: () => ({
densityBtn: true,
fullScreenBtn: true,
}),
},
// 是否开启斑马纹
openStripe: {
type: Boolean,
default: true,
},
},
1.5.2 表格组件发送的事件 emit
emits: [
'table-sort-change', // 表格列排序事件
'table-choose-change', // 表格行选中事件
'table-cell-click', // 单元格点击事件
'update:fullScreen', // 更新全屏状态
],
表格列排序事件:需要根据业务,重新调整拼接出来的排序依据字段 orderBy
// 表格排序接受的参数
interface TableSortColumn {
order: string;
prop: string;
column: {
property: string;
sortable: string;
label: string;
[key: string]: unknown;
};
}
/**
* 表格排序
* @param sortColumn
*/
const onSortChange = (sortColumn: CommonBaseCrud.TableSortColumn) => {
let orderBy = '';
if (sortColumn && sortColumn.column && sortColumn.column.sortable) {
orderBy = `${sortColumn.column.sortable} ${sortColumn.order.replace('ending', '')}`;
} else {
orderBy = '';
}
emit('table-sort-change', orderBy);
};
表格行选中事件:将选择的表格行数据抛出去
// 响应式数据
const state = reactive({
// 用户选择的列表项数组
multipleSelection: [] as any,
});
/**
* 表格多选改变
* @description 当选择多条表格数据时,被选中的数据会被存储到 state 响应数据中去
* @param val 当前选中的全部表格数据(以数组的形式进行存储)
*/
const onSelectChange = (val: any) => {
state.multipleSelection = val;
emit('table-choose-change', state.multipleSelection);
};
单元格点击事件:将点击的单元格数据抛出去
/**
* 处理单元格点击事件
*/
const onCellClick = (row: any, column: any, event: any, cell: any) => {
emit('table-cell-click', { row, column, event, cell });
};
1.5.3 表格顶部操作行
左侧按钮(表格密度、表格全屏)
右侧按钮组(添加表格数据、多条删除、下载导出、授权等)
<el-row
v-if="showOperationColumn"
align="middle"
justify="space-between"
type="flex"
>
<!-- 左侧按钮组(密度、全屏) -->
<el-col :span="8">
<!-- 表格密度设置按钮 -->
<t-density-button @table-size-change="setTableDensity" />
<!-- 表格全屏设置按钮 -->
<t-full-screen-button @full-screen-change="handleSetFullScreen" />
</el-col>
<!-- 右侧按钮组 -->
<el-col :span="16">
<div class="flex flex-end">
<!-- 表格按钮组 - 插槽 -->
<slot name="tableBtnSlot"></slot>
</div>
</el-col>
</el-row>
设置表格尺寸(密度)、控制全屏状态:
// 响应式数据
const state = reactive({
// 表格尺寸
tableSize: 'mini',
// 是否全屏
fullScreen: false,
});
/**
* 设置表格尺寸(密度)
* @param command 表格尺寸(密度)
*/
const setTableDensity = (command: string) => {
state.tableSize = command;
setTableBodyTop();
};
/**
* 控制全屏状态
*/
const handleSetFullScreen = (val: any) => {
state.fullScreen = val;
emit('update:fullScreen', val);
};
element 计算的高度,没有带上小数,导致边框线被压着,需要重新设置(此方法在 onMounted 、重新设置表格密度等 需要重新渲染表格 的状态下,需要执行)
/**
* element 计算的高度,没有带上小数,导致边框线被压着,需要重新设置
*/
const setTableBodyTop = () => {
setTimeout(() => {
const tableDom = document.getElementsByClassName('el-table') as any;
if (tableDom && tableDom.length > 0) {
for (let i = 0; i < tableDom.length; i++) {
// 获取高度
const tableHeaderDom = tableDom[i].getElementsByClassName('el-table__header-wrapper')[0];
const tableHeaderHeight = tableHeaderDom.getBoundingClientRect().height + 'px';
// 设置top值
const tableBody = tableDom[i].getElementsByClassName('el-table__fixed-body-wrapper');
if (tableBody && tableBody[0]) {
tableBody[0].style.top = tableHeaderHeight;
}
if (tableBody && tableBody[1]) {
tableBody[1].style.top = tableHeaderHeight;
}
const tablePatch = tableDom[i].getElementsByClassName('el-table__fixed-right-patch');
if (tablePatch && tablePatch[0]) {
tablePatch[0].style.height = tableHeaderHeight;
}
}
}
}, 200);
};
1.5.4 表格模板封装
选择列(多选按钮):
- 根据接收的 props openSelectColumn 决定是否渲染;
- 根据 selectable 决定是否为选择列,此列永远固定在首列;
序号列:
- 根据接收的 props openIndexColumn && pageSize && pageNum 决定是否渲染;
- 此列内容固定,采用 (pageNum - 1) * pageSize + scope.$index + 1 动态计算当前行索引;
普通列:
- 使用 template 遍历 props tableNormalOptions 表头配置;
- 给 el-table-column 动态绑定 key、prop、label、width、min-width、align、header-align、fixed、sortable、show-overflow-tooltip、resizable 等属性;
- 关于 resizable 属性 —— 对应列是否可以通过拖动改变宽度(需要在 el-table 上设置 border 属性为真)
普通列插槽:
- 如果使用插槽,则用户可以自定义展示内容
- 如果不用插槽,则默认对展示信息做判空处理,并增加 title 鼠标滑过显示完整信息的属性
<!-- 自定义列内容格式 -->
<template v-if="tOptions.slot" #default="scope">
<slot :name="tOptions.slot" :scope="scope"></slot>
</template>
<!-- 默认展示格式(此处做了判空处理, 如果为空, 则展示小短横线) -->
<template v-else #default="scope">
<span :title="`${isNoVal(scope.row[tOptions.prop])}`">
{{ `${numFont(isNoVal(scope.row[tOptions.prop]))}` }}
</span>
</template>
/**
* 是否有值
*/
const isNoVal = (val?: any) => {
let newVal = val;
if (!val && val !== 0) {
newVal = '-';
}
return newVal;
};
文字溢出隐藏:
- 如果用户使用了插槽,则不展示 tooltip(因为合并数据很多的话,会导致 tooltip 显示在最顶部,影响展示效果)
- 如果用户没有使用插槽,则根据是否超过 20 个字,决定是否展示 tooltip;超过 20 个字了就手动截断(这个是根据项目业务决定的)
:show-overflow-tooltip="tOptions.slot ? false : tooltipBool"
// 响应式数据
const state = reactive({
// 控制tooltip的显示
tooltipBool: false,
});
/**
* 值是否超过20个字 超过用...代替
*/
const numFont = (val?:string) => {
let newVal = val;
if (newVal) {
if (newVal.length > 20) {
const vals = newVal.slice(21);
newVal = newVal.replace(vals, '...');
state.tooltipBool = true;
} else {
state.tooltipBool = false;
}
}
return newVal;
};
多级表头:根据表头配置单项中,是否包含 child 属性决定,整体实现逻辑和单表头一致
操作列:不用于表格顶部的操作列,表格内部的操作列通常是针对单行数据进行操作,所以它里面直接放了个插槽
如何区分表格 操作列 / 普通列 的数据呢?他们都是放在 tableNormalOptions 中的,参考下方:
// 过滤 操作项 之后的列配置
const tableNormalOptions = computed(() => props.tableHeaderOpitons.filter((item) => !item.action));
// 操作项列配置
const tableActionOptions = computed(() => props.tableHeaderOpitons.find((item) => item.action));
/*
* console.log('过滤 操作项 之后的配置', tableNormalOptions.value);
* console.log('操作项', tableActionOptions.value);
*/
整体效果:
<el-table
ref="tableRef"
:data="tableData"
:cell-style="changeCellStyle"
:span-method="spanMethod"
:size="tableSize"
tooltip-effect="dark"
sortable
border
stripe
v-bind="$attrs"
@selection-change="onSelectChange"
@sort-change="onSortChange"
@cell-click="onCellClick"
>
<!-- 选择列(多选按钮) -->
<el-table-column
v-if="openSelectColumn"
:type="'selection'"
:header-align="'center'"
:align="'center'"
:fixed="'left'"
:selectable="selectable"
:resizable="false"
></el-table-column>
<!-- 序号列 -->
<el-table-column
v-if="openIndexColumn && pageSize && pageNum"
:type="'index'"
:align="'center'"
:fixed="'left'"
:label="'序号'"
:width="55"
:min-width="55"
:resizable="false"
>
<template #default="scope">
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<!-- 普通列 -->
<template
v-for="(tOptions, tIndex) of tableNormalOptions"
:key="tIndex"
>
<!-- 单表头 -->
<el-table-column
v-if="!tOptions.child"
:key="tIndex"
:prop="tOptions.prop"
:label="tOptions.label"
:width="tOptions.width ? tOptions.width : ''"
:min-width="tOptions.minWidth ? tOptions.minWidth : ''"
:align="tOptions.align ? tOptions.align : 'center'"
:header-align="tOptions.headerAlign ? tOptions.headerAlign : 'center'"
:fixed="tOptions.fixed ? tOptions.fixed : false"
:sortable="tOptions.sortable ? tOptions.sortable : false"
:show-overflow-tooltip="tOptions.slot ? false : tooltipBool"
:resizable="tOptions.resizable !== null ? tOptions.resizable : true"
>
<!-- 自定义列内容格式 -->
<template v-if="tOptions.slot" #default="scope">
<slot :name="tOptions.slot" :scope="scope"></slot>
</template>
<!-- 默认展示格式(此处做了判空处理, 如果为空, 则展示小短横线) -->
<template v-else #default="scope">
<span :title="`${isNoVal(scope.row[tOptions.prop])}`">
{{ `${numFont(isNoVal(scope.row[tOptions.prop]))}` }}
</span>
</template>
</el-table-column>
<!-- 多表头 -->
<el-table-column v-else :label="tOptions.label" :header-align="'center'">
<el-table-column
v-for="(childOptions, cIndex) of tOptions.child"
:key="cIndex"
:prop="childOptions.prop"
:label="childOptions.label"
:width="childOptions.width ? childOptions.width : ''"
:min-width="childOptions.minWidth ? childOptions.minWidth : ''"
:align="childOptions.align ? childOptions.align : 'center'"
:header-align="childOptions.headerAlign ? childOptions.headerAlign : 'center'"
:sortable="childOptions.sortable ? childOptions.sortable : false"
:show-overflow-tooltip="childOptions.slot ? false : true"
:resizable="childOptions.resizable !== null ? childOptions.resizable : true"
>
<!-- 自定义列内容格式 -->
<template v-if="childOptions.slot" #default="scope">
<slot :name="childOptions.slot" :scope="scope"></slot>
</template>
<!-- 默认展示格式(此处做了判空处理, 如果为空, 则展示小短横线) -->
<template v-else #default="scope">
<span :title="`${isNoVal(scope.row[childOptions.prop])}`">
{{ `${isNoVal(scope.row[childOptions.prop])}` }}
</span>
</template>
</el-table-column>
</el-table-column>
</template>
<!-- 操作列 -->
<el-table-column
v-if="tableActionOptions"
:width="tableActionOptions.width ? tableActionOptions.width : ''"
:min-width="tableActionOptions.minWidth ? tableActionOptions.minWidth : ''"
:header-align="'center'"
:align="'center'"
:fixed="tableActionOptions.fixed ? tableActionOptions.fixed : 'right'"
label="操作"
:resizable="false"
>
<template #default="scope">
<slot :name="'actionSolt'" :scope="scope"></slot>
</template>
</el-table-column>
</el-table>
2. vxe
vxe-table v4
https://vxetable.cn/#/table/scroll/scroll
2.1 功能介绍
针对 elementplus 数据量大时,页面卡顿的问题,推荐使用 vxe-table 解决(因为免费^_^)
虚拟滚动(最大可以支撑 10w 列、30w 行)
为了实现组件的通用性,vxe-table 组件的 全屏按钮、密度控制按钮、表格组件接收的 props、表格组件发出的事件 emits 等封装逻辑,基本参考 1 中的内容,此处不做赘述
优点:性能比 ElementPlus 更高
缺点:当合并行高度 超过 表格高度时,会出现一片空白
2.2 vxe-table 表格组件
2.2.1 vxe-table 数据重载
要等 vxe 组件加载完成后,渲染数据,也就是执行 vxe.reloadData(tableData);
const xTable = ref({} as any);
onMounted(() => {
const tableRef = xTable.value;
if (tableRef) {
console.log(tableRef, ' === vxe 表格实例 tableReftableReftableRef');
tableRef.reloadData(props.tableData);
}
});
// 监听数据的变化,重新加载数据
watch(
() => props.tableData,
() => {
const tableRef = xTable.value;
if (tableRef) {
tableRef.reloadData(props.tableData);
}
},
{
deep: true,
},
);
vxe-table 重载 reloadData() 执行时,还原滚动条 scrollTop 的位置:
const scrollY = this.$refs.showTable.$refs.xTable.$el.scrollTop;
this.$nextTick(() => {
this.$refs.showTable.reloadData(this.tableData);
setTimeout(() => {
this.$refs.showTable.$refs.xTable.$el.scrollTop = scrollY;
}, 100);
})
注意事项:
- scrollTop 的值,需要精准的定位到滚动条所属的标签内,这样才能获得正确的值,否则都是 0
- 不能缺少 nextTick,如果还是不生效,就加一下 setTimeout
2.2.2 表格组件发送的事件 emit
表格行选中事件:将选择的表格行数据抛出去
/**
* 表格多选改变
* @description 当选择多条表格数据时,被选中的数据会被存储到 state 响应数据中去
* @param val 当前选中的全部表格数据(以数组的形式进行存储)
*/
const onSelectChange = (val: any) => {
if (Array.isArray(val)) {
state.multipleSelection = val;
} else {
state.multipleSelection = val.records;
}
emit('table-choose-change', state.multipleSelection);
};
单元格点击事件:将点击的单元格数据抛出去
/**
* 处理单元格点击事件
*/
const onCellClick = (cellInfos: any) => {
emit('table-cell-click', cellInfos);
};
2.2.3 vxe-table 常见 API
row-config —— 行配置信息
|
useKey |
是否需要为每一行的 VNode 设置 key 属性(非特殊情况下不需要使用) |
boolean |
|
false |
v4.2.0 |
|
keyField |
自定义行数据唯一主键的字段名(默认自动生成) |
string |
|
_X_ROW_KEY |
v4.2.0 |
|
isCurrent |
当鼠标点击行时,是否要高亮当前行 |
boolean |
|
false |
v4.1.5 |
|
isHover |
当鼠标移到行时,是否要高亮当前行 |
boolean |
|
false |
v4.1.5 |
|
height |
只对 show-overflow 有效,每一行的高度 |
number |
|
|
|
seq-config —— 序号配置项
|
startIndex |
请使用 seqMethod |
number |
|||
|
seqMethod |
自定义序号的方法,返回处理后的值 |
({ row, rowIndex, column, columnIndex }) => number |
|
:seq-config="{ seqMethod: setSeqStartIndex }"
/**
* @description: 设置序列号的值
* @param {*} data 单条数据
* @return {*}返回处理后的值
*/
const setSeqStartIndex = (data: { rowIndex: number }) => {
const { rowIndex } = data;
return (props.pageNum - 1) * props.pageSize + rowIndex + 1;
};
column-config —— 列配置信息
|
useKey |
是否需要为每一列的 VNode 设置 key 属性(非特殊情况下不需要使用) |
boolean |
|
||
|
isCurrent |
当鼠标点击列头时,是否要高亮当前列 |
boolean |
|
||
|
isHover |
当鼠标移到列头时,是否要高亮当前头 |
boolean |
|
||
|
resizable |
每一列是否启用列宽调整 |
boolean |
false |
||
|
width |
每一列的宽度 |
number, string |
auto, px, % |
||
|
minWidth |
每一列的最小宽度 |
number, string |
auto, px, % |
2.2.4 表格模板封装
<vxe-table
ref="xTable"
:size="tableSize"
border
stripe
show-overflow
height="500px"
max-height="500px"
:row-config="{ isHover: true, useKey: true }"
:seq-config="{ seqMethod: setSeqStartIndex }"
:column-config="{ resizable: true }"
v-bind="$attrs"
:span-method="spanMethod"
@sort-change="onSortChange"
@checkbox-change="onSelectChange"
@checkbox-all="onSelectChange"
@cell-click="onCellClick"
>
<!-- 选择列(多选按钮) -->
<vxe-column
v-if="openSelectColumn"
type="checkbox"
width="48"
align="center"
fixed="left"
:resizable="false"
></vxe-column>
<!-- 序号列 -->
<vxe-column
v-if="openIndexColumn && pageSize && pageNum"
type="seq"
title="序号"
width="52"
fixed="left"
align="center"
:resizable="false"
>
</vxe-column>
<!-- 普通列 -->
<template v-for="(tOptions, tIndex) of tableNormalOptions" :key="tIndex">
<!-- 单表头 -->
<vxe-column
v-if="!tOptions.child"
:key="tIndex"
:title="tOptions.label"
:field="tOptions.prop"
:width="tOptions.width ? tOptions.width : ''"
:min-width="tOptions.minWidth ? tOptions.minWidth : ''"
:header-align="tOptions.headerAlign ? tOptions.headerAlign : 'center'"
:align="tOptions.align ? tOptions.align : 'center'"
:fixed="tOptions.fixed ? tOptions.fixed : ''"
:sortable="tOptions.sortable ? true : false"
:sort-by="tOptions.sortable ? tOptions.sortable : ''"
:resizable="tOptions.resizable !== null ? tOptions.resizable : true"
>
<!-- 自定义列内容格式 -->
<template v-if="tOptions.slot" #default="scope">
<slot :name="tOptions.slot" :scope="scope"></slot>
</template>
<!-- 默认展示格式(此处做了判空处理, 如果为空, 则展示小短横线) -->
<template v-else #default="scope">
{{ `${isNoVal(scope.row[tOptions.prop])}` }}
</template>
</vxe-column>
<!-- 多表头 -->
<vxe-colgroup
v-else
:title="tOptions.label"
:header-align="tOptions.headerAlign ? tOptions.headerAlign : 'center'"
>
<vxe-column
v-for="(childOptions, cIndex) of tOptions.child"
:key="cIndex"
:title="childOptions.label"
:field="childOptions.prop"
:width="childOptions.width ? childOptions.width : ''"
:min-width="childOptions.minWidth ? childOptions.minWidth : ''"
:header-align="childOptions.headerAlign ? childOptions.headerAlign : 'center'"
:align="childOptions.align ? childOptions.align : 'center'"
:fixed="childOptions.fixed ? childOptions.fixed : ''"
:sortable="childOptions.sortable ? true : false"
:sort-by="childOptions.sortable ? childOptions.sortable : ''"
:resizable="childOptions.resizable !== null ? childOptions.resizable : true"
:span-method="spanMethod"
>
<!-- 自定义列内容格式 -->
<template v-if="childOptions.slot" #default="scope">
<slot :name="childOptions.slot" :scope="scope"></slot>
</template>
<!-- 默认展示格式(此处做了判空处理, 如果为空, 则展示小短横线) -->
<template v-else #default="scope">
{{ `${isNoVal(scope.row[childOptions.prop])}` }}
</template>
</vxe-column>
</vxe-colgroup>
</template>
<!-- 操作列 -->
<vxe-column
v-if="tableActionOptions"
:width="tableActionOptions.width ? tableActionOptions.width : ''"
:min-width="tableActionOptions.minWidth ? tableActionOptions.minWidth : ''"
title="操作"
fixed="right"
align="center"
:resizable="false"
>
<template #default="scope">
<slot :name="'actionSolt'" :scope="scope"></slot>
</template>
</vxe-column>
</vxe-table>
<style>
.vxe-table--fixed-right-wrapper{
height: 100% !important;
}
</style>
3. surely-table
Surely Vue
https://www.surely.cool/
3.1 功能介绍
// surely-table
npm i --save @surely-vue/table
// xe-utils JavaScript 函数库、工具类
npm install xe-utils
// ant-design-vue
npm install ant-design-vue --save
优点:多行合并不再卡顿
缺点: 收费(╯T□T)╯︵┻━┻,当然了免费也可以用,但是会有水印及控制台报错……
参考:commit bf2ac1e8f190cab892b4ae285427206bf2a40ced 添加大数据量表格(合并)示例
3.2 surely-table 表格组件
3.2.1 表格组件发送的事件 emit
单元格点击事件:将点击的单元格数据抛出去
/**
* 处理单元格点击事件
*/
const handleCellClick = (record:any) => ({
onClick: () => {
emit('table-cell-click', record);
}, // 点击行
});
3.2.2 surely-table 常见 API
bordered —— 是否展示外边框和列边框 boolean
columns —— 表格列的配置描述 array
https://www.surely.cool/doc/api#column
https://www.surely.cool/doc/api#column
dataSource —— 数据数组 object[]
scroll —— 表格是否可滚动,也可以指定滚动区域的宽、高 object
pagination —— 分页器
rowKey —— 表格行 key 的取值,可以是字符串或一个函数 string|Function(record):string
customCell —— 设置单元格属性, column 如配置了 customCell, 优先使用 column.customCell Function(obj: {record: any; rowIndex: number; column: ColumnType})
autoHeaderHeight —— 是否自动表头高度,开启后会全量加载表头部分,有一定的性能损耗
3.2.3 表格模板封装
使用这个表格主要是为了轻松实现合并,所以这个表格不加 多选、操作列 了
<s-table
:animate-rows="false"
bordered
:columns="tableHeaderOpitons"
:data-source="tableData"
:scroll="{ y: 400 }"
:pagination="false"
:row-key="(record,index)=>{return index}"
:custom-cell="handleCellClick"
stripe
auto-header-height
size="small"
>
<template #bodyCell="{ column, record, index }">
<slot v-if="column.slot" :name="column.slot" :scope="{ column, record, index }"></slot>
<template v-else>
{{ `${numFont(isNoVal(record[column.dataIndex]))}` }}
</template>
</template>
</s-table>
3.2.4 调整 surely-table 样式
.surely-table-wrapper {
/* 外边框 */
.surely-table-bordered {
border: 1px solid var(--border-color1);
}
/* 表头颜色 */
.surely-table-header-cell {
background-color: var(--table-header-background-color);
}
/* 鼠标滑过颜色 */
.surely-table-row.surely-table-row-hover {
background: none !important;
}
.surely-table-row.surely-table-row-hover .surely-table-cell:not(.surely-table-cell-hidden) {
background-color: var(--table-hover-background) !important;
}
/* 斑马线颜色 */
.surely-table.surely-table-stripe .surely-table-body .surely-table-row-odd:not(.surely-table-row-selected) {
background: none;
}
.surely-table.surely-table-stripe .surely-table-body .surely-table-row-odd:not(.surely-table-row-selected) .surely-table-cell:not(.surely-table-cell-hidden) {
background-color: var(--table-stripe-background);
}
.surely-table-fix-right {
background: #FFF;
}
.surely-table-cell {
border-color: var(--border-color1);
}
.surely-table-header {
position: relative;
z-index: 5;
margin-bottom: -1px;
}
.surely-table-header-scrollbar {
background: var(--table-header-background-color);
&:after {
background: var(--border-color1);
}
}
.surely-table-body {
.surely-table-cell {
border-top: 1px solid var(--border-color1);
border-bottom: none;
}
.surely-table-center {
.surely-table-cell {
border-left: 1px solid var(--border-color1);
}
.surely-table-cell:not(:last-child) {
margin-left: -1px;
border-right: none;
}
}
}
.surely-table-header-cell {
color: var(--font-color1);
font-size: 0.875rem;
}
.surely-table-row {
color: var(--font-color2);
font-size: 0.875rem;
}
}
.surely-table-cell-hidden {
border: none !important;
visibility: initial;
}
.surely-table-cell-hidden .surely-table-cell-inner {
visibility: hidden;
}
.surely-table > div:not([class]),
.surely-table-body > div:not([class]) {
visibility: hidden;
}
// 提示框
.ant-tooltip {
font-size: 12px;
.ant-tooltip-inner {
min-height: auto;
}
}
更多推荐


所有评论(0)