operation-system/src/components/tableComponent.vue

410 lines
9.3 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" v-if="showPagination">
<el-form :inline="true" class="demo-form-inline" :label-width="'auto'">
<el-form-item label="每页显示" style="margin-bottom: 10px">
<el-select
v-model="computedPageSize"
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; text-align: center"
:max-height="tableMaxHeight"
v-loading="loading"
:data="tableData"
:border="showBorder"
:stripe="showStripe"
:fit="true"
v-bind="$attrs"
:header-cell-class-name="headerCellClassName"
:cell-class-name="cellClassName"
@selection-change="handleSelectionChange"
>
<!-- 树形展开 -->
<el-table-column v-if="tree" type="expand">
<template #default="props">
<slot name="tree" :row="props.row"></slot>
</template>
</el-table-column>
<!-- 首列多选框 -->
<el-table-column
v-if="showSelection"
type="selection"
width="30"
align="center"
fixed
/>
<el-table-column label="排序" width="50" v-if="showSort" fixed>
<template #default="{ $index }">
{{ (currentPage - 1) * pageSize + $index + 1 }}
</template>
</el-table-column>
<template v-for="column in columns" :key="column.prop">
<el-table-column
:prop="column.prop"
:label="column.label"
:width="column.width"
:align="column.align || 'center'"
:fixed="column.fixed ?? false"
: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">
{{ currentPage }}{{
Math.ceil(total / computedPageSize)
}}{{ total }}
</span>
<div
style="
flex: 1;
display: flex;
justify-content: end;
text-align: right;
padding-right: 120px;
"
>
<el-pagination
v-model:current-page="computedCurrentPage"
v-model:page-size="computedPageSize"
:page-sizes="pageSizes"
:small="small"
:disabled="disabled"
:background="background"
layout="prev, pager, next, jumper"
:total="total"
@current-change="handleCurrentChange"
/>
<span class="custom-pagination-size">
{{ computedPageSize }}/
</span>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, computed, onMounted, onBeforeUnmount } from "vue";
const props = defineProps({
loading: {
type: Boolean,
default: false,
},
// 表格数据
tableData: {
type: Array,
default: () => [],
},
// 列配置
columns: {
type: Array,
default: () => [],
},
tree: {
type: Boolean,
default: false,
},
// 当前页码(由外部传入)
currentPage: {
type: Number,
default: 1,
},
// 每页数量(由外部传入)
pageSize: {
type: Number,
default: 10,
},
// 总条数
total: {
type: Number,
default: 0,
},
// 是否显示分页
showPagination: {
type: Boolean,
default: true,
},
// 是否显示边框
showBorder: {
type: Boolean,
default: false,
},
// 是否显示斑马纹
showStripe: {
type: Boolean,
default: false,
},
// 是否显示首列多选框
showSelection: {
type: Boolean,
default: false,
},
// 是否展示序号
showSort: {
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: () => "custom-header",
},
// 自定义单元格类名函数
cellClassName: {
type: Function,
default: () => "",
},
rowkey: {
type: String,
default: "",
},
});
const emit = defineEmits([
"update:currentPage",
"update:pageSize",
"page-change",
"selection-change",
]);
// 每页数量(双向绑定)
const computedPageSize = computed({
get: () => props.pageSize,
set: (val) => emit("update:pageSize", val),
});
// 当前页码(双向绑定)
const computedCurrentPage = computed({
get: () => props.currentPage,
set: (val) => emit("update:currentPage", val),
});
// 分页大小改变
const pageSizeChange = (val) => {
// console.log(`每页 ${val} 条`);
// console.log(props.currentPage);
emit("update:pageSize", val);
emit("page-change", {
page: props.currentPage,
pageSize: val,
});
};
// 当前页改变
const handleCurrentChange = (val) => {
// console.log(`当前页改变 ${val} 页`);
emit("page-change", {
page: val,
pageSize: props.pageSize,
});
};
// 多选框变化
const handleSelectionChange = (selection) => {
const selectedIds = selection.map((row) => props.rowkey == "" ? row.id : row[props.rowkey]);
emit("selection-change", selection, selectedIds);
};
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;
padding-top: 4px;
}
.pagination-container {
margin-top: 10px;
padding: 0 30px 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(237 255 248) !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;
position: absolute;
right: 16px;
bottom: 12px;
color: #999;
}
}
}
</style>