operation-system/src/components/tableComponent.vue

358 lines
8.2 KiB
Vue
Raw Normal View History

<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="每页显示" style="margin-bottom: 10px;">
<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>
</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;
font-size: 14px;
}
.custom-pagination-size {
text-align: right;
line-height: 32px;
margin-left: 20px;
font-size: 14px;
}
/* 去除表格边框 */
: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>