feat: enhance dashboard layout with fixed height components and improved styling for media cards

This commit is contained in:
jxxghp
2026-06-28 09:05:31 +08:00
parent 165247b263
commit 1889d5d1e3
12 changed files with 171 additions and 44 deletions

View File

@@ -130,6 +130,18 @@ const enableConfig = ref<{ [key: string]: boolean }>({
latest: true,
})
// 仅声明了默认行高的内置仪表盘组件使用固定 grid 高度,插件和空媒体组件继续按内容自适应。
const fixedHeightDashboardIds = new Set([
'storage',
'mediaStatistic',
'weeklyOverview',
'speed',
'scheduler',
'cpu',
'memory',
'network',
])
// 仪表板顺序配置
const orderConfig = ref<{ id: string; key: string }[]>([])
@@ -141,7 +153,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 4 },
rows: 5,
rows: 11,
elements: [],
},
{
@@ -150,7 +162,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 8 },
rows: 5,
rows: 11,
elements: [],
},
{
@@ -159,7 +171,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 4 },
rows: 11,
rows: 23,
elements: [],
},
{
@@ -168,7 +180,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 4 },
rows: 11,
rows: 23,
elements: [],
},
{
@@ -177,7 +189,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 4 },
rows: 11,
rows: 23,
elements: [],
},
{
@@ -186,7 +198,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 6 },
rows: 8,
rows: 17,
elements: [],
},
{
@@ -195,7 +207,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 6 },
rows: 8,
rows: 17,
elements: [],
},
{
@@ -204,7 +216,7 @@ const dashboardConfigs = ref<DashboardItem[]>([
key: '',
attrs: {},
cols: { cols: 12, md: 6 },
rows: 8,
rows: 17,
elements: [],
},
{
@@ -396,7 +408,8 @@ function buildRemoteDashboardGridLayout(layout: DashboardGridLayoutConfig) {
// 根据当前视口判断仪表板布局档位,避免手机和桌面共用 Grid 坐标。
function resolveDashboardLayoutProfile(): DashboardLayoutProfile {
const width = display.width.value || (typeof window === 'undefined' ? DASHBOARD_GRID_DESKTOP_BREAKPOINT : window.innerWidth)
const width =
display.width.value || (typeof window === 'undefined' ? DASHBOARD_GRID_DESKTOP_BREAKPOINT : window.innerWidth)
if (width <= DASHBOARD_GRID_MOBILE_BREAKPOINT) return 'mobile'
if (width <= DASHBOARD_GRID_TABLET_BREAKPOINT) return 'tablet'
@@ -1123,7 +1136,10 @@ watch(
dashboardLayoutProfile.value = nextProfile
dashboardGridLayout.value = (await loadDashboardGridLayoutConfig(nextProfile)) ?? {}
dashboardGrid.value?.column(getDashboardGridColumnsForProfile(nextProfile), getDashboardGridColumnLayout(nextProfile))
dashboardGrid.value?.column(
getDashboardGridColumnsForProfile(nextProfile),
getDashboardGridColumnLayout(nextProfile),
)
dashboardGrid.value?.removeAll(false, false)
await syncDashboardGrid()
notifyDashboardContentResize()
@@ -1184,7 +1200,10 @@ onBeforeUnmount(() => {
v-for="gridItem in dashboardGridItems"
:key="gridItem.id"
class="grid-stack-item dashboard-grid-item"
:class="{ 'is-manual-height': hasManualDashboardGridHeight(gridItem.id) }"
:class="{
'is-manual-height': hasManualDashboardGridHeight(gridItem.id),
'is-fixed-height': fixedHeightDashboardIds.has(gridItem.config.id) && !gridItem.config.key,
}"
:gs-id="gridItem.id"
:gs-x="gridItem.widget.x"
:gs-y="gridItem.widget.y"
@@ -1270,6 +1289,8 @@ onBeforeUnmount(() => {
inline-size: 100%;
}
.dashboard-grid-item.is-fixed-height .dashboard-grid-auto-size,
.dashboard-grid-item.is-fixed-height .dashboard-grid-content-measure,
.dashboard-grid-item.is-manual-height .dashboard-grid-auto-size,
.dashboard-grid-item.is-manual-height .dashboard-grid-content-measure,
.dashboard-grid.is-editing .dashboard-grid-auto-size,
@@ -1281,34 +1302,6 @@ onBeforeUnmount(() => {
block-size: 100%;
}
.dashboard-grid :deep(.dashboard-chart-card),
.dashboard-grid :deep(.dashboard-summary-card),
.dashboard-grid :deep(.dashboard-work-card),
.dashboard-grid :deep(.dashboard-media-card) {
display: flex;
flex-direction: column;
min-block-size: 0;
}
.dashboard-grid :deep(.dashboard-chart-card .v-card-text),
.dashboard-grid :deep(.dashboard-work-card .v-card-text),
.dashboard-grid :deep(.dashboard-card-grid-wrap) {
flex: 1 1 auto;
min-block-size: 0;
}
.dashboard-grid:not(.is-editing) .dashboard-grid-item:not(.is-manual-height) :deep(.dashboard-summary-card) {
min-block-size: 160px;
}
.dashboard-grid:not(.is-editing) .dashboard-grid-item:not(.is-manual-height) :deep(.dashboard-chart-card) {
min-block-size: 256px;
}
.dashboard-grid:not(.is-editing) .dashboard-grid-item:not(.is-manual-height) :deep(.dashboard-work-card) {
min-block-size: 352px;
}
.dashboard-grid.is-editing :deep(.v-card-text),
.dashboard-grid-item.is-manual-height :deep(.v-card-text) {
overflow: auto;

View File

@@ -154,11 +154,19 @@ useKeepAliveRefresh(refresh)
</template>
<style scoped>
.dashboard-chart-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.dashboard-chart-content {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: hidden;
}
.dashboard-chart-plot {

View File

@@ -87,7 +87,7 @@ onActivated(() => {
<VCardTitle>{{ t('dashboard.mediaStatistic') }}</VCardTitle>
</VCardItem>
<VCardText>
<VCardText class="dashboard-summary-content">
<VRow>
<VCol v-for="item in statistics" :key="item.title" cols="6" sm="3">
<div class="d-flex align-center">
@@ -111,6 +111,25 @@ onActivated(() => {
</template>
<style lang="scss" scoped>
.dashboard-summary-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.dashboard-summary-content {
display: flex;
flex: 1 1 auto;
align-items: center;
min-block-size: 0;
}
.dashboard-summary-content :deep(.v-row) {
flex: 1 1 auto;
align-items: center;
}
.dashboard-number {
font-variant-numeric: tabular-nums;
}

View File

@@ -160,11 +160,19 @@ useKeepAliveRefresh(refresh)
</template>
<style scoped>
.dashboard-chart-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.dashboard-chart-content {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: hidden;
}
.dashboard-chart-plot {

View File

@@ -196,11 +196,19 @@ useKeepAliveRefresh(refresh)
</template>
<style scoped>
.dashboard-chart-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.dashboard-chart-content {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: hidden;
}
.dashboard-chart-plot {

View File

@@ -83,6 +83,13 @@ useDataRefresh(
</template>
<style lang="scss" scoped>
.dashboard-work-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.card-list {
--v-card-list-gap: 1.5rem;
@@ -96,6 +103,7 @@ useDataRefresh(
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: hidden;
}
.card-list::-webkit-scrollbar {

View File

@@ -146,6 +146,13 @@ const { loading } = useDataRefresh(
</template>
<style lang="scss" scoped>
.dashboard-work-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.card-list {
--v-card-list-gap: 1rem;
@@ -159,6 +166,7 @@ const { loading } = useDataRefresh(
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: hidden;
}
.dashboard-speed-number {

View File

@@ -69,7 +69,7 @@ onActivated(() => {
<VCardItem>
<VCardTitle>{{ t('dashboard.storage') }}</VCardTitle>
</VCardItem>
<VCardText>
<VCardText class="dashboard-summary-content">
<h5 class="animated-storage-value text-2xl font-weight-medium text-primary">
{{ animatedStorageText }}
</h5>
@@ -106,6 +106,20 @@ onActivated(() => {
inset-inline-end: 2rem;
}
.dashboard-summary-card {
position: relative;
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
overflow: hidden;
}
.dashboard-summary-content {
flex: 1 1 auto;
min-block-size: 0;
}
.animated-storage-value {
font-variant-numeric: tabular-nums;
}

View File

@@ -156,11 +156,19 @@ onActivated(() => {
</template>
<style scoped>
.dashboard-work-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.dashboard-work-content {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: hidden;
}
.dashboard-work-chart {

View File

@@ -62,7 +62,7 @@ onActivated(() => {
<VCardTitle>{{ t('dashboard.latest') }} - {{ name }}</VCardTitle>
</VCardItem>
<div class="px-5 pb-3">
<div class="dashboard-media-content px-5 pb-3">
<ProgressiveCardGrid
class="dashboard-media-grid"
:items="data"
@@ -91,7 +91,10 @@ onActivated(() => {
}
.dashboard-media-stack > .dashboard-media-card {
display: flex;
flex-direction: column;
flex: 1 1 auto;
block-size: 100%;
min-block-size: 0;
}
@@ -99,4 +102,16 @@ onActivated(() => {
flex: 1 1 auto;
min-block-size: 0;
}
.dashboard-media-content {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: auto;
}
.dashboard-media-content::-webkit-scrollbar {
display: none;
}
</style>

View File

@@ -65,7 +65,7 @@ onActivated(() => {
<VCardItem>
<VCardTitle>{{ t('dashboard.library') }}</VCardTitle>
</VCardItem>
<div class="px-5 pb-3">
<div class="dashboard-media-content px-5 pb-3">
<ProgressiveCardGrid
class="dashboard-media-grid"
:items="libraryList"
@@ -89,4 +89,23 @@ onActivated(() => {
flex: 1 1 auto;
min-block-size: 0;
}
.dashboard-media-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.dashboard-media-content {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: auto;
}
.dashboard-media-content::-webkit-scrollbar {
display: none;
}
</style>

View File

@@ -66,7 +66,7 @@ onActivated(() => {
<VCardTitle>{{ t('dashboard.playing') }}</VCardTitle>
</VCardItem>
<div class="px-5 pb-3">
<div class="dashboard-media-content px-5 pb-3">
<ProgressiveCardGrid
class="dashboard-media-grid"
:items="playingList"
@@ -90,4 +90,23 @@ onActivated(() => {
flex: 1 1 auto;
min-block-size: 0;
}
.dashboard-media-card {
display: flex;
flex-direction: column;
block-size: 100%;
min-block-size: 0;
}
.dashboard-media-content {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-block-size: 0;
overflow: auto;
}
.dashboard-media-content::-webkit-scrollbar {
display: none;
}
</style>