336 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="custom-table-container">
<div class="custom-table-tool">
<el-form :inline="true" class="demo-form-inline" :label-width="'auto'">
<el-form-item label="每页显示">
<el-select v-model="internalPageSize" placeholder="请选择" style="width: 100px" @change="pageSizeChange($event)">
<el-option v-for="item in pageSizes" :key="item" :label="item" :value="item" />
</el-select>
<span style="padding: 0 10px"></span>
</el-form-item>
</el-form>
</div>
<el-table
style="flex: 1; display: flex; flex-direction: column"
:max-height="tableMaxHeight"
:data="pagedTableData"
:border="showBorder"
:stripe="showStripe"
v-bind="$attrs"
:header-cell-class-name="headerCellClassName"
:cell-class-name="cellClassName"
@selection-change="handleSelectionChange"
>
<!-- 首列多选框 -->
<el-table-column v-if="showSelection" type="selection" width="55" align="center" />
<template v-for="column in columns" :key="column.prop">
<el-table-column
:prop="column.prop"
:label="column.label"
:width="column.width"
:align="column.align || 'left'"
:sortable="column.sortable"
:header-class-name="column.headerClassName"
>
<!-- 支持插槽 -->
<template v-if="column.slotName" #default="scope">
<slot :name="column.slotName" :row="scope.row"></slot>
</template>
</el-table-column>
</template>
</el-table>
<div v-if="showPagination" class="pagination-container">
<span class="custom-pagination-text">
{{ internalPage }}{{ Math.ceil(internalTotal / internalPageSize) }}{{ internalTotal }}
</span>
<div style="flex: 1; display: flex; justify-content: end; text-align: right">
<el-pagination
v-model:current-page="internalPage"
v-model:page-size="internalPageSize"
:page-sizes="pageSizes"
:small="small"
:disabled="disabled"
:background="background"
layout="prev, pager, next"
:total="internalTotal"
@current-change="handleCurrentChange"
/>
<span class="custom-pagination-size"> {{ internalPageSize }}/ </span>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, computed, onMounted, onBeforeUnmount } from 'vue';
const props = defineProps({
// 表格数据
tableData: {
type: Array,
default: () => [],
},
// 列配置
columns: {
type: Array,
default: () => [],
},
// 是否显示分页
showPagination: {
type: Boolean,
default: true,
},
// 是否显示边框
showBorder: {
type: Boolean,
default: false,
},
// 是否显示斑马纹
showStripe: {
type: Boolean,
default: false,
},
// 是否显示首列多选框
showSelection: {
type: Boolean,
default: false,
},
// 每页显示条数选项
pageSizes: {
type: Array,
default: () => [10, 20, 30, 50],
},
// 是否使用小型分页样式
small: {
type: Boolean,
default: false,
},
// 是否禁用
disabled: {
type: Boolean,
default: false,
},
// 是否为分页按钮添加背景色
background: {
type: Boolean,
default: false,
},
// 自定义表头类名函数
headerCellClassName: {
type: Function,
default: () => '',
},
// 自定义单元格类名函数
cellClassName: {
type: Function,
default: () => '',
},
});
const emit = defineEmits(['page-change', 'selection-change']);
// 内部管理的分页状态
const internalPage = ref(1);
const internalPageSize = ref(props.pageSizes?.[0] || 10);
const internalTotal = ref(0);
// 计算当前页显示的数据
const pagedTableData = computed(() => {
const start = (internalPage.value - 1) * internalPageSize.value;
const end = start + internalPageSize.value;
return props.tableData.slice(start, end);
});
// 计算总页数(可根据实际需求调整)
watch(
() => props.tableData,
(newData) => {
internalTotal.value = newData.length; // 示例假设总数据量是当前显示的3倍
},
{ immediate: true }
);
// 分页大小改变
const pageSizeChange = (val) => {
console.log(`每页 ${val}`);
internalPageSize.value = val;
internalPage.value = 1; // 重置为第一页
console.log(internalPage.value, internalPageSize.value);
emitPageChange();
};
// 当前页改变
const handleCurrentChange = (val) => {
console.log(`当前页改变 ${val}`);
internalPage.value = val;
emitPageChange();
};
// 触发分页变化事件
const emitPageChange = () => {
emit('page-change', {
page: internalPage.value,
pageSize: internalPageSize.value,
});
};
// 多选框变化
const handleSelectionChange = (selection) => {
emit('selection-change', selection);
};
const tableRef = ref(null);
const tableMaxHeight = ref(null); // 使用max-height而不是height
// 自动计算最大高度(预留分页器空间)
const calculateMaxHeight = () => {
const paginationHeight = 60; // 分页器固定高度
const container = tableRef.value?.$el?.parentElement;
if (container) {
const containerHeight = container.clientHeight;
tableMaxHeight.value = containerHeight - paginationHeight;
}
};
onMounted(() => {
calculateMaxHeight();
window.addEventListener('resize', calculateMaxHeight);
// 添加MutationObserver监听父容器尺寸变化
const observer = new MutationObserver(calculateMaxHeight);
if (tableRef.value?.$el?.parentElement) {
observer.observe(tableRef.value.$el.parentElement, {
attributes: true,
attributeFilter: ['style', 'class'],
});
}
});
onBeforeUnmount(() => {
window.removeEventListener('resize', calculateMaxHeight);
});
</script>
<style lang="scss" scoped>
.custom-table-container {
// position: relative;
padding: 10px 0;
display: flex;
flex-direction: column;
height: 100%; /* 关键:继承父容器高度 */
overflow: hidden; /* 防止内容溢出 */
}
/* 表格弹性布局 */
:deep(.el-table) {
flex: 1;
display: flex;
flex-direction: column;
/* 表头固定 */
.el-table__header-wrapper {
flex-shrink: 0;
}
/* 表体可滚动 */
.el-table__body-wrapper {
flex: 1;
overflow: auto;
}
}
.demo-form-inline {
text-align: left;
padding-left: 20px;
}
.pagination-container {
margin-top: 10px;
padding: 0 20px;
display: flex;
justify-content: space-between;
color: #999;
font-weight: 400;
}
.custom-pagination-text {
flex: 1;
text-align: left;
line-height: 32px;
}
.custom-pagination-size {
text-align: right;
line-height: 32px;
margin-left: 20px;
}
/* 去除表格边框 */
:deep(.el-table) {
--el-table-border-color: transparent;
}
/* 自定义鼠标悬停颜色 */
:deep(.el-table__body tr:hover > td) {
background-color: rgba(37, 191, 130, 0.1) !important;
}
/* 自定义表头样式 */
:deep(.custom-header) {
background-color: #fff !important;
color: #999;
font-weight: 400;
}
:deep(.el-pagination) {
/* 整体分页样式 */
font-size: 14px;
font-weight: normal;
/* 页码按钮容器 */
.el-pager {
/* 所有页码项 */
li {
font-weight: 400; /* 普通页码字体不加粗 */
color: #606266; /* 普通页码颜色 */
background: transparent;
font-size: 14px;
/* 当前选中页码 */
&.active,
&.is-active {
font-weight: 400 !important; /* 当前页不加粗 */
color: #25bf82 !important; /* 自定义当前页颜色 - 橙色示例 */
}
/* 悬停状态 */
&:hover {
color: #409eff;
}
}
}
/* 上一页/下一页按钮 */
.btn-prev,
.btn-next {
font-weight: 400;
&:disabled {
color: #c0c4cc;
}
}
/* 每页条数选择器 */
.el-pagination__sizes {
.el-input__inner {
font-weight: 400;
}
}
/* 跳页输入框 */
.el-pagination__jump {
font-weight: 400;
}
}
</style>