|
|
@@ -0,0 +1,127 @@
|
|
|
+import {
|
|
|
+ nextTick,
|
|
|
+ onBeforeUnmount,
|
|
|
+ onMounted,
|
|
|
+ ref,
|
|
|
+ unref,
|
|
|
+ watch,
|
|
|
+ type Ref
|
|
|
+} from 'vue'
|
|
|
+import { useEventListener, useResizeObserver } from '@vueuse/core'
|
|
|
+import {
|
|
|
+ LIST_TABLE_VIEWPORT_BOTTOM_GAP,
|
|
|
+ LIST_TABLE_VIEWPORT_MIN_HEIGHT,
|
|
|
+ calcListTableViewportHeight
|
|
|
+} from '@/composables/listTableViewportHeight'
|
|
|
+
|
|
|
+export type UseListTableViewportHeightOptions = {
|
|
|
+ /** 表格锚点(包住 ListTable/el-table 的元素,不含分页) */
|
|
|
+ tableEl: Ref<HTMLElement | null | undefined> | (() => HTMLElement | null | undefined)
|
|
|
+ /** 分页条根节点;无分页可省略 */
|
|
|
+ pagerEl?: Ref<HTMLElement | null | undefined> | (() => HTMLElement | null | undefined)
|
|
|
+ /** 可选:ListTable / el-table 组件实例,高度变化后调用 doLayout */
|
|
|
+ tableRef?: Ref<any>
|
|
|
+ minHeight?: number
|
|
|
+ bottomGap?: number
|
|
|
+ /** 主内容滚动根;默认 #app-content */
|
|
|
+ scrollRootSelector?: string
|
|
|
+ /** 为 false 时不自动监听(隐藏 Tab 可用) */
|
|
|
+ enabled?: Ref<boolean> | boolean
|
|
|
+}
|
|
|
+
|
|
|
+function resolveEl(
|
|
|
+ source?: Ref<HTMLElement | null | undefined> | (() => HTMLElement | null | undefined)
|
|
|
+): HTMLElement | null {
|
|
|
+ if (!source) return null
|
|
|
+ if (typeof source === 'function') return source() ?? null
|
|
|
+ return unref(source) ?? null
|
|
|
+}
|
|
|
+
|
|
|
+export function useListTableViewportHeight(options: UseListTableViewportHeightOptions) {
|
|
|
+ const tableHeight = ref(LIST_TABLE_VIEWPORT_MIN_HEIGHT)
|
|
|
+ const minHeight = options.minHeight ?? LIST_TABLE_VIEWPORT_MIN_HEIGHT
|
|
|
+ const bottomGap = options.bottomGap ?? LIST_TABLE_VIEWPORT_BOTTOM_GAP
|
|
|
+ const scrollRootSelector = options.scrollRootSelector ?? '#app-content'
|
|
|
+
|
|
|
+ const isEnabled = () => {
|
|
|
+ const e = options.enabled
|
|
|
+ if (e === undefined) return true
|
|
|
+ return !!unref(e)
|
|
|
+ }
|
|
|
+
|
|
|
+ const recalc = () => {
|
|
|
+ if (!isEnabled()) return
|
|
|
+ if (typeof window === 'undefined') return
|
|
|
+
|
|
|
+ const table = resolveEl(options.tableEl)
|
|
|
+ if (!table) return
|
|
|
+
|
|
|
+ const root =
|
|
|
+ (document.querySelector(scrollRootSelector) as HTMLElement | null) ?? null
|
|
|
+ const contentViewportBottom = root
|
|
|
+ ? root.getBoundingClientRect().bottom
|
|
|
+ : window.innerHeight
|
|
|
+
|
|
|
+ const tableTop = table.getBoundingClientRect().top
|
|
|
+ const pager = resolveEl(options.pagerEl)
|
|
|
+ const pagerHeight = pager ? Math.ceil(pager.getBoundingClientRect().height) : 0
|
|
|
+
|
|
|
+ const next = calcListTableViewportHeight({
|
|
|
+ contentViewportBottom,
|
|
|
+ tableTop,
|
|
|
+ pagerHeight,
|
|
|
+ minHeight,
|
|
|
+ bottomGap
|
|
|
+ })
|
|
|
+
|
|
|
+ if (next !== tableHeight.value) {
|
|
|
+ tableHeight.value = next
|
|
|
+ }
|
|
|
+
|
|
|
+ nextTick(() => {
|
|
|
+ const inst = options.tableRef ? unref(options.tableRef) : null
|
|
|
+ if (inst && typeof inst.doLayout === 'function') {
|
|
|
+ inst.doLayout()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ let raf = 0
|
|
|
+ const scheduleRecalc = () => {
|
|
|
+ if (raf) cancelAnimationFrame(raf)
|
|
|
+ raf = requestAnimationFrame(() => {
|
|
|
+ raf = 0
|
|
|
+ recalc()
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ nextTick(scheduleRecalc)
|
|
|
+ })
|
|
|
+
|
|
|
+ onBeforeUnmount(() => {
|
|
|
+ if (raf) cancelAnimationFrame(raf)
|
|
|
+ })
|
|
|
+
|
|
|
+ useEventListener(window, 'resize', scheduleRecalc)
|
|
|
+
|
|
|
+ // 观察表锚点与分页尺寸变化(筛选展开会改变 tableTop)
|
|
|
+ useResizeObserver(
|
|
|
+ () => resolveEl(options.tableEl),
|
|
|
+ () => scheduleRecalc()
|
|
|
+ )
|
|
|
+ useResizeObserver(
|
|
|
+ () => resolveEl(options.pagerEl),
|
|
|
+ () => scheduleRecalc()
|
|
|
+ )
|
|
|
+ useResizeObserver(
|
|
|
+ () => document.querySelector(scrollRootSelector) as HTMLElement | null,
|
|
|
+ () => scheduleRecalc()
|
|
|
+ )
|
|
|
+
|
|
|
+ if (options.enabled !== undefined && typeof options.enabled !== 'boolean') {
|
|
|
+ watch(options.enabled, () => scheduleRecalc())
|
|
|
+ }
|
|
|
+
|
|
|
+ return { tableHeight, recalc: scheduleRecalc }
|
|
|
+}
|