2026-08-10 15:36:53 +08:00
|
|
|
<script setup lang="ts">
|
2026-08-10 16:24:54 +08:00
|
|
|
import { Comment, Fragment, Text, isVNode, useSlots } from 'vue'
|
|
|
|
|
|
2026-08-10 15:36:53 +08:00
|
|
|
withDefaults(defineProps<{
|
|
|
|
|
ariaLabel?: string
|
|
|
|
|
}>(), {
|
|
|
|
|
ariaLabel: '列表页面',
|
|
|
|
|
})
|
2026-08-10 16:24:54 +08:00
|
|
|
|
|
|
|
|
const slots = useSlots()
|
|
|
|
|
type RegionName = 'toolbar' | 'metrics' | 'state' | 'default'
|
|
|
|
|
|
|
|
|
|
function hasRenderableContent(value: unknown): boolean {
|
|
|
|
|
if (Array.isArray(value)) return value.some(hasRenderableContent)
|
|
|
|
|
if (!isVNode(value)) return typeof value === 'string' ? value.trim().length > 0 : value != null && typeof value !== 'boolean'
|
|
|
|
|
if (value.type === Comment) return false
|
|
|
|
|
if (value.type === Text) return hasRenderableContent(value.children)
|
|
|
|
|
if (value.type === Fragment) return hasRenderableContent(value.children)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hasRegion(name: RegionName) {
|
|
|
|
|
return hasRenderableContent(slots[name]?.())
|
|
|
|
|
}
|
2026-08-10 15:36:53 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<section class="list-page-layout" :aria-label="ariaLabel">
|
2026-08-10 16:24:54 +08:00
|
|
|
<div v-if="hasRegion('toolbar')" class="list-page-layout__toolbar"><slot name="toolbar" /></div>
|
|
|
|
|
<div v-if="hasRegion('metrics')" class="list-page-layout__metrics"><slot name="metrics" /></div>
|
|
|
|
|
<div v-if="hasRegion('state')" class="list-page-layout__state"><slot name="state" /></div>
|
|
|
|
|
<div v-if="hasRegion('default')" class="list-page-layout__content"><slot /></div>
|
2026-08-10 15:36:53 +08:00
|
|
|
</section>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.list-page-layout { display: flex; min-width: 0; flex-direction: column; gap: 16px; }
|
|
|
|
|
.list-page-layout__toolbar, .list-page-layout__metrics, .list-page-layout__state, .list-page-layout__content { min-width: 0; }
|
2026-08-10 16:24:54 +08:00
|
|
|
.list-page-layout__toolbar:empty, .list-page-layout__metrics:empty, .list-page-layout__state:empty, .list-page-layout__content:empty { display: none; }
|
2026-08-10 15:36:53 +08:00
|
|
|
</style>
|