Files
onboard/resources/js/Components/PageBanner.vue
brusnitsyn 739168d427 Обновлен стартовый экран
Переписаны запросы для статистики, отчетов
Добавлена интеграция отчета сестры
2026-05-28 22:10:00 +09:00

113 lines
4.5 KiB
Vue

<script setup>
import { NEl, NFlex, NIcon, NText } from 'naive-ui'
import { TbChevronRight } from 'vue-icons-plus/tb'
import { useThemeVars } from 'naive-ui'
import { computed } from 'vue'
const props = defineProps({
title: { type: String, default: '' },
// null / имя цвета темы (primary|success|error|warning|info) / 'default' = нейтральный тёмный
color: { type: String, default: null },
// [{ label, href? }]
breadcrumbs: { type: Array, default: () => [] },
// компонент-иконка (если не нужен кастомный слот #icon)
icon: { type: [Object, Function], default: null },
})
const themeVars = useThemeVars()
const isNeutral = computed(() => props.color === 'default')
const cssColor = computed(() => isNeutral.value ? null : `var(--${props.color ?? 'primary'}-color)`)
const bannerBg = computed(() => isNeutral.value
? themeVars.value.cardColor
: `color-mix(in srgb, ${cssColor.value} 8%, ${themeVars.value.cardColor})`
)
const bannerBorder = computed(() => isNeutral.value
? themeVars.value.dividerColor
: `color-mix(in srgb, ${cssColor.value} 20%, transparent)`
)
const iconBg = computed(() => isNeutral.value
? `color-mix(in srgb, ${themeVars.value.textColor1} 10%, transparent)`
: `color-mix(in srgb, ${cssColor.value} 16%, transparent)`
)
const iconColor = computed(() => isNeutral.value
? themeVars.value.textColor2
: cssColor.value
)
const crumbColor = computed(() => isNeutral.value
? themeVars.value.primaryColor
: cssColor.value
)
</script>
<template>
<NEl
class="rounded-2xl px-6 py-5"
:style="`background: ${bannerBg}; border: 1px solid ${bannerBorder};`"
>
<!-- Хлебные крошки -->
<NFlex v-if="breadcrumbs.length" align="center" :size="6" style="margin-bottom: 12px;">
<template v-for="(crumb, i) in breadcrumbs" :key="i">
<NEl
v-if="crumb.href"
:tag="crumb.tag ?? 'a'"
:href="crumb.href"
:style="`font-size: 12px; color: ${crumbColor}; text-decoration: none; opacity: .75; transition: opacity .15s;`"
@mouseenter="e => e.currentTarget.style.opacity = 1"
@mouseleave="e => e.currentTarget.style.opacity = .75"
>
<NFlex align="center" :size="4">
<NIcon v-if="crumb.icon" size="13"><component :is="crumb.icon" /></NIcon>
{{ crumb.label }}
</NFlex>
</NEl>
<NText v-else depth="3" style="font-size: 12px;">{{ crumb.label }}</NText>
<NIcon v-if="i < breadcrumbs.length - 1" size="12" depth="3"><TbChevronRight /></NIcon>
</template>
</NFlex>
<!-- Основная строка -->
<NFlex align="center" justify="space-between" :wrap="false">
<NFlex align="center" :size="14" :wrap="false">
<!-- Иконка: кастомный слот или prop -->
<NEl
v-if="$slots.icon || icon"
class="rounded-xl"
:style="`
width: 48px; height: 48px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
background: ${iconBg};
`"
>
<slot name="icon">
<NIcon :size="24" :style="`color: ${iconColor};`">
<component :is="icon" />
</NIcon>
</slot>
</NEl>
<div class="min-w-0">
<!-- Заголовок: кастомный слот или prop -->
<slot name="title">
<span style="font-size: 20px; font-weight: 700; line-height: 1.2;">
{{ title }}
</span>
</slot>
<!-- Мета-строка -->
<div v-if="$slots.meta" style="margin-top: 5px;">
<slot name="meta" />
</div>
</div>
</NFlex>
<!-- Действия (кнопки) -->
<NFlex v-if="$slots.actions" :size="8" style="flex-shrink: 0;">
<slot name="actions" />
</NFlex>
</NFlex>
</NEl>
</template>