Files
Thing/src/components/layout/ModuleContainer.vue
T
2026-09-18 18:28:13 +08:00

108 lines
3.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { Component } from 'vue'
import { ref, watch } from 'vue'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Skeleton } from '@/components/ui/skeleton'
const props = defineProps<{
activeComponent: Component | null
activeModule: string
loading: boolean
}>()
const containerRef = ref<HTMLElement | null>(null)
// 切换模块时重置主滚动区位置,避免新模块沿用上一个模块的滚动距离
watch(
() => props.activeModule,
() => {
const viewport = containerRef.value?.querySelector<HTMLElement>(
'[data-slot="scroll-area-viewport"]'
)
if (viewport) viewport.scrollTop = 0
}
)
</script>
<template>
<main
ref="containerRef"
class="flex-1 min-w-0"
:style="{ backgroundColor: 'var(--effect-bg)', backdropFilter: 'var(--effect-blur)' }"
>
<ScrollArea data-main-scroll class="h-full w-full">
<Transition name="fade-slide" mode="out-in">
<div
v-if="activeComponent"
:key="activeModule"
class="h-full w-full"
>
<component :is="activeComponent" />
</div>
<div
v-else-if="loading"
key="loading"
class="h-full w-full p-6"
>
<!-- 模块加载骨架撑起画面避免空白闪屏 -->
<div class="h-full max-w-5xl mx-auto space-y-5">
<div class="flex items-center gap-3">
<Skeleton class="h-8 w-40" />
<Skeleton class="h-6 w-24 ml-auto" />
</div>
<div class="grid gap-4 md:grid-cols-2">
<div v-for="n in 4" :key="n" class="rounded-lg border p-5 space-y-4">
<div class="flex items-center justify-between">
<Skeleton class="h-5 w-32" />
<Skeleton class="h-5 w-16" />
</div>
<Skeleton class="h-4 w-full" />
<Skeleton class="h-4 w-5/6" />
<Skeleton class="h-4 w-2/3" />
</div>
</div>
</div>
</div>
<div
v-else
key="empty"
class="h-full w-full flex items-center justify-center text-muted-foreground"
>
<p>未找到模块</p>
</div>
</Transition>
</ScrollArea>
</main>
</template>
<style scoped>
/*
* 穿透 reka-ui ScrollArea 的内部 content wrapper。
*
* reka 的 viewportoverflow 滚动容器)与我们的内容之间还有一层**无高度样式的
* div**,百分比高度链在这里断掉——模块根的 `h-full` 解析为 auto,全高模块
* (终端/翻译)塌缩成内容高度,表现为卡片下方留白。给它显式 100%:
* 内容矮于视口时撑满,高于视口时溢出仍由 viewport 滚动(scrollHeight 计入
* 后代溢出),两种场景都不破坏。限定 data-main-scroll 只作用于主滚动区,
* 不影响模块内部的局部 ScrollArea。
*/
:deep([data-main-scroll] [data-reka-scroll-area-viewport] > div) {
height: 100%;
}
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.3s ease;
}
.fade-slide-enter-from {
opacity: 0;
transform: translateX(20px);
}
.fade-slide-leave-to {
opacity: 0;
transform: translateX(-20px);
}
</style>