mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-03 13:31:34 +08:00
feat: enhance dashboard layout with fixed height components and improved styling for media cards
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user