开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
引言
之前接手的项目有很多前辈二次封装的东西,闲的没事儿做的时候我就会去看看,发现别人写的代码确实比自己的好很多,运用了许多封装思想,前段时间写了很多表格,用到了前辈的表格组件,自己也学习了下,新增了一个表格跨页全选功能。
二次封装表格和分页组件
首先可以看到 Element-plus
提供的表格组件拥有非常多的属性,我只是根据需求,封装了一些需要用到的东西,大家也可以根据自己的需求自行封装。
在components
中创建一个公共组件,命名为 myTable.vue
<template>
<!-- 是否显示表头的全选 -->
<div :class="{isSingle:props.isSingle}">
<el-table
:header-cell-style="props.headerCellStyle"
:header-row-style="props.headerRowStyle"
:data="props.tableData"
:max-height="props.maxHeight"
:size="props.size"
ref="tableRef"
style="width: 100%"
:highlight-current-row="props.highlightCurrentRow"
@current-change="changeCurrent"
@selection-change="changeSelect"
@select="changeSelectTap"
@select-all="changeSelectAll"
:row-key="props.rowKey"
:tree-props="props.treeProps||treeProps"
>
<!-- 是否开启多选功能 -->
<el-table-column v-if="props.isSelect" :reserve-selection="true" type="selection" width="50"/>
<!-- 遍历表头数据 -->
<template v-for="(item, index) in props.header">
<el-table-column
:key="index"
:align="item.align || 'center'"
:sortable="item.sortable"
:width="item.width"
:min-width="item.minWidth"
:label="item.label"
:prop="item.prop"
:fixed="item.fixed"
v-if="item.isShow == false ? false : true"
show-overflow-tooltip
>
<!-- 自定义行slot -->
<template v-if="item.isCustom" #default="scope">
<slot :name="item.prop" :row="scope.row" :column="scope.column" :index="scope.$index"></slot>
</template>
</el-table-column>
</template>
</el-table>
<!-- 分页,传了分页数据就会展示 -->
<div v-if="page" class="mt15 flex-center">
<el-pagination
:currentPage="props.page?.pageIndex"
:page-size="props.page?.pageSize"
:page-sizes="[10, 20, 30]"
layout="sizes, prev, pager, next, jumper"
:total="props.page?.total"
:background="true"
:hide-on-single-page="false"
@size-change="changeSize"
@current-change="changePage"
>
</el-pagination>
</div>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import { ElTable,ElPagination } from "element-plus";
//表格实例
const tableRef=ref();
//表格数据的配置项
interface Header {
label: string;
prop?: string;
width?: string | number;
minWidth?: string | number;
sortable?: boolean;
align?: string;
isCustom?: boolean;
fixed?: string;
isShow?: boolean;
}
//分页数据配置项
interface Page {
total?: number;
pageSize?: number;
pageIndex?: number;
background?: boolean;
}
//props配置项
interface Props {
tableData: Array<any>; //表格数据
page?: Page; //分页数据
isSelect?: boolean; //是否支持多选
isSingle?: boolean; //是否隐藏全选
size?: 'large' | 'default' | 'small'; //table的尺寸
header: Array<Header>; //表头数据
maxHeight?: string | number;
headerRowStyle?: Function | object;
headerCellStyle?: Function | object;
highlightCurrentRow?: boolean;
rowKey?: string,
treeProps?: Function |object
}
const treeProps={ children: 'children', hasChildren: 'hasChildren' }
//设置props默认配置项
const props = withDefaults(defineProps<Props>(), {
isSelect: false,
isOperation: true,
size: 'default',
highlightCurrentRow: false,
rowKey: 'id',
isSingle:false
});
// 每页条数 改变时触发
const changeSize = (size: number) => {
emit('changeSize', size);
}
//翻页时触发
const changePage = (page: number) => {
emit('changePage', page);
}
// 选择行
const changeCurrent = (currentRow: any, oldCurrentRow: any) => {
emit('changeCurrent', {currentRow, oldCurrentRow})
}
// 不管全选还是勾选都会触发
const changeSelect = (val: any) => {
emit('onSelect', val)
}
// 手动全选
const changeSelectAll = (val: any) => {
emit('onSelectAll', val)
}
// 手动勾选
const changeSelectTap = (val: any,row:any) => {
emit('onSelectTap', val,row)
}
//让父组件可以获取到table实例
defineExpose({ tableRef})
//注册事件
const emit = defineEmits(['changeSize', 'changePage', 'changeCurrent', 'onSelect','onSelectTap','onSelectAll']);
</script>
<style lang="scss" scoped>
.isSingle{
:deep(*){
.el-table__header{
.el-checkbox{
visibility: hidden;
}
}
}
}
</style>
代码中有很多注释,一些不知道含义的配置项可以对照
Element-plus
官网查阅,其实最主要的只有三个配置项,表头数据,表格数据和分页数据,其它都是为了增加表格功能和美化表格而增加,大家可以根据需求自行增删。组件封装好了,接下来就是使用和实现跨页全选功能。
使用组件
在 App.vue
中引入 MyTable
组件,定义好表头数据,表格数据和分页数据
<template>
<MyTable
ref="tableRef"
:page="tableData.page"
:isSelect="true"
:rowKey="'id'"
@changeSize="(size) => changeSize(size)"
@changePage="(page) => changePage(page)"
:header="header"
:tableData="
tableData.data.slice(
(tableData.page.pageIndex - 1) * tableData.page.pageSize,
tableData.page.pageIndex * tableData.page.pageSize
)
"
@onSelectTap="changeSelectTap"
@onSelectAll="changeSelectAll"
>
<template v-slot:sex="scope">
{{ scope.row.sex == "0" ? "男" : "女" }}
</template>
<template v-slot:operation="scope">
<el-button @click="handelEdit(scope.row)">编辑</el-button>
<el-button @click="handelDel(scope.row)">删除</el-button>
</template>
</MyTable>
</template>
<script lang="ts" setup>
import MyTable from "./components/myTable.vue";
import { ref, reactive, onMounted } from "vue";
const tableRef = ref();
// 表头
const header = reactive([
{
label: "姓名",
prop: "name",
},
{
label: "性别",
prop: "sex",
isCustom: true,
},
{
label: "年龄",
prop: "age",
},
{
label: "操作",
prop: "operation",
isCustom: true,
fixed: "right",
align: "center",
width: 200,
},
]);
// 表格数据
const tableData = reactive({
data: [],
searchForm: {},
page: {
pageIndex: 1,
pageSize: 10,
total: 500,
},
});
const handelEdit = (val) => {
console.log("编辑");
};
const handelDel = (val) => {
console.log("删除");
};
onMounted(() => {
//模拟请求数据
setTimeout(() => {
//随机生成一些表格数据
for (let i = 0; i < 500; i++) {
let obj = {
id:i,
name: `c${i + 1}`,
sex: parseInt(String(Math.random() * 2)),
age: Math.round(Math.random() * 100),
};
tableData.data.push(obj);
}
}, 1500);
});
</script>
<style scoped></style>
这个时候的表格页面应该长成这样,可以看到页面最左边出现了多选框,这个时候点击表头的全选时只会选择当前的所有数据
实现跨页全选
//已被勾选的数据id
const ids = ref('');
//多选
const changeSelectTap = (val: any) => {
ids.value = val.map((v: any) => v.id).join(',');
};
//是否全选
const isSelectAll = ref(false);
const changeSelectAll = (val: any) => {
if (!isSelectAll.value) {
isSelectAll.value = true;
ids.value = tableData.data.map((v: any) => v.id).join(',');
console.log(ids.value);
} else {
ids.value = '';
isSelectAll.value = false;
console.log(ids.value);
}
};
//修改每页的数据数量
const changeSize = (size: any) => {
tableData.page.pageIndex = 1;
tableData.page.pageSize = size;
let selectData = ids.value.split(",");
tableData.data.forEach((v: any) => {
let index = selectData.findIndex((t) => {
return v.id == t;
});
tableRef.value.tableRef.toggleRowSelection(v, index != -1);
});
};
//修改当前页
const changePage = (page: any) => {
tableData.page.pageIndex = page;
let selectData = ids.value.split(",");
tableData.data.forEach((v: any) => {
let index = selectData.findIndex((t) => {
return v.id === t;
});
tableRef.value.tableRef.toggleRowSelection(v, index != -1);
});
};
上面代码中,有一个很重要的方法
toggleRowSelection
,可以看上方官网对它的解释,当触发勾选事件的时候,将已勾选的数据id
保存起来,定义isSelectAll
来控制是否全选,控制ids
清空;当全选事件触发后,会把所有数据的id
保存起来,当改变每页显示的条数或者切换页数时,通过toggleRowSelection
来控制多选框的状态。至此,跨页全选就实现了。