frontend: let the list tables fill the window instead of their rows

A list page is a table with controls around it, and the table was as tall as
its rows: on a large screen most of the window sat empty below the last row
while the page stayed the thing that scrolled. The three list pages now stretch
the table to the height the window offers — the toolbar and the pagination stay
put and the rows scroll inside the table — and give the height back on a small
screen.

Pure CSS, no resize listener and no measured pixel height, so nothing goes
stale when the rail collapses or the window is resized. Four links carry it,
each load-bearing: `.page--fill` is `min-height: 100%` (a floor, not a cap),
`.card--fill` makes the card a column, its body gets `min-height: 0` so a flex
child may shrink below its content, and `.table-fill` is `flex: 1 1 0` so the
table takes the leftover height rather than claiming its content height as its
basis. Element Plus then pins the header and scrolls the rows.

Verified by rendering the pages in a headless browser at 1920x1080, 1440x900
and 1280x620: the table measures 781/601/321px, the page never scrolls, the
rows scroll inside it, header and body columns stay aligned to 0px through a
400px horizontal scroll, the pinned action column stays pinned, and the empty
state is centred in the full-height body rather than stranded at the top. A
1280x420 window hits the 240px floor and falls back to the page scrolling, and
the paper document page is untouched.
This commit is contained in:
2026-09-18 18:26:52 +08:00
parent a5f884f440
commit 61857c3ce1
5 changed files with 104 additions and 9 deletions
+57
View File
@@ -202,3 +202,60 @@ samp {
justify-content: flex-end;
margin-top: 16px;
}
/* --- list pages that fill the viewport --------------------------------------
A list page is a table with controls around it. Left alone, the table is as
tall as its rows, so on a large screen most of the window sits empty under
the last row while the page is still the thing that scrolls. These three
classes make the table take the height the window offers instead: more rows
visible at once on a big screen, and the height given back on a small one.
Every link in the chain is load-bearing:
.page--fill `min-height`, never `height`
.card--fill a column, so its body can pass the height on
.card--fill > .el-card__body
a column too, and `min-height: 0` — without it
a flex child refuses to shrink below its
content and the table would never get a height
.table-fill `flex: 1 1 0`, i.e. basis 0. With `auto` the
table would claim its content height as its
basis and push the pagination out of the card
el-table height="100%" fills .table-fill; Element Plus then keeps the
header fixed and scrolls the rows internally
`.page--fill` is a *minimum*, not a fixed height: on a short window the
table keeps its 240px floor, the page grows past the viewport and the
content area scrolls, which is the behaviour a squeezed table would not
survive. */
.page--fill {
min-height: 100%;
}
.card--fill {
/* Basis auto, so the card fills the page's minimum height exactly and still
grows if its content needs more. */
flex: 1 1 auto;
display: flex;
flex-direction: column;
min-height: 0;
}
.card--fill > .el-card__header {
flex: 0 0 auto;
}
.card--fill > .el-card__body {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
.table-fill {
/* `1 1 0` rather than `1 1 auto`: the table takes the leftover height rather
than adding to it. The min-height is the floor a short window lands on. */
flex: 1 1 0;
min-height: 240px;
}
+4 -3
View File
@@ -168,8 +168,8 @@ onMounted(async () => {
</script>
<template>
<div class="page">
<el-card shadow="never">
<div class="page page--fill">
<el-card shadow="never" class="card--fill">
<template #header>
<div class="card-header">
<div>
@@ -230,7 +230,8 @@ onMounted(async () => {
:data="rows"
row-key="id"
stripe
class="table"
class="table table-fill"
height="100%"
@selection-change="(value: PaperListItem[]) => (selection = value)"
>
<el-table-column type="selection" width="46" reserve-selection />
@@ -135,8 +135,8 @@ onMounted(load)
</script>
<template>
<div class="page">
<el-card shadow="never">
<div class="page page--fill">
<el-card shadow="never" class="card--fill">
<template #header>
<div class="card-header">
<div>
@@ -193,7 +193,8 @@ onMounted(load)
:data="rows"
row-key="id"
stripe
class="table"
class="table table-fill"
height="100%"
@selection-change="(value: TemplateFieldLibrary[]) => (selection = value)"
>
<el-table-column type="selection" width="46" reserve-selection />
@@ -140,8 +140,8 @@ onMounted(load)
</script>
<template>
<div class="page">
<el-card shadow="never">
<div class="page page--fill">
<el-card shadow="never" class="card--fill">
<template #header>
<div class="card-header">
<div>
@@ -187,7 +187,8 @@ onMounted(load)
:data="rows"
row-key="id"
stripe
class="table"
class="table table-fill"
height="100%"
@selection-change="(value: TemplateListItem[]) => (selection = value)"
>
<el-table-column type="selection" width="46" reserve-selection />