diff --git a/client/src/composables/useTableScroll.ts b/client/src/composables/useTableScroll.ts new file mode 100644 index 0000000..875ee2e --- /dev/null +++ b/client/src/composables/useTableScroll.ts @@ -0,0 +1,51 @@ +import { ref, onMounted, onBeforeUnmount } from 'vue'; + +/** + * 监听表格父容器高度,动态计算 a-table 的 scroll.y。 + * 自动测量 a-table 内部的分页器高度,从而得到准确的 tbody 高度。 + */ +export function useTableScroll(minHeight = 120) { + const wrapperRef = ref(null); + const tableHeight = ref(minHeight); + let observer: ResizeObserver | null = null; + + function update() { + if (!wrapperRef.value) return; + const wrapperHeight = wrapperRef.value.clientHeight; + + // 动态获取分页器高度(如果存在) + const paginationEl = wrapperRef.value.querySelector('.ant-table-pagination') as HTMLElement | null; + const paginationHeight = paginationEl ? paginationEl.offsetHeight : 0; + + // scroll.y = 容器总高 - 分页器高度(表头由 a-table 自动固定,无需扣除) + const tbodyHeight = wrapperHeight - paginationHeight; + tableHeight.value = Math.max(tbodyHeight, minHeight); + } + + onMounted(() => { + if (!wrapperRef.value) return; + observer = new ResizeObserver(update); + observer.observe(wrapperRef.value); + + // 初始测量 + update(); + + // MutationObserver 用于监听分页器的动态添加/移除 + const mutationObserver = new MutationObserver(() => { + update(); + }); + mutationObserver.observe(wrapperRef.value, { childList: true, subtree: true }); + // 保存以便卸载时使用 + (wrapperRef.value as any).__mutationObserver = mutationObserver; + }); + + onBeforeUnmount(() => { + observer?.disconnect(); + observer = null; + if (wrapperRef.value && (wrapperRef.value as any).__mutationObserver) { + (wrapperRef.value as any).__mutationObserver.disconnect(); + } + }); + + return { wrapperRef, tableHeight }; +} diff --git a/client/src/layouts/index.vue b/client/src/layouts/index.vue index bbb9510..69299cd 100644 --- a/client/src/layouts/index.vue +++ b/client/src/layouts/index.vue @@ -409,13 +409,17 @@ function handleUserMenuClick({ key }: { key: string }) { .content { margin: 0; - min-height: calc(100vh - 64px); + height: calc(100vh - 64px); + overflow: hidden; .view-wrapper { background: #fff; padding: 0; border-radius: 0; - min-height: calc(100vh - 64px); + height: 100%; + display: flex; + flex-direction: column; + overflow: hidden; box-shadow: none; } } diff --git a/client/src/views/log/operation/index.vue b/client/src/views/log/operation/index.vue index 4dff79e..2689618 100644 --- a/client/src/views/log/operation/index.vue +++ b/client/src/views/log/operation/index.vue @@ -63,12 +63,14 @@ +
+
@@ -92,6 +95,9 @@ import { message } from 'ant-design-vue' import { SearchOutlined, ReloadOutlined, DownloadOutlined } from '@ant-design/icons-vue' import dayjs from 'dayjs' import { logApi } from '@/api/log' +import { useTableScroll } from '@/composables/useTableScroll' + +const { wrapperRef, tableHeight } = useTableScroll() const tableLoading = ref(false) const exportLoading = ref(false) @@ -271,19 +277,46 @@ onMounted(() => {