107 lines
3.1 KiB
Vue
107 lines
3.1 KiB
Vue
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
import { NModal, NInput, NScrollbar, NGrid, NGi, NText } from 'naive-ui'
|
||
import { TbSearch } from 'vue-icons-plus/tb'
|
||
import PresetCard from './PresetCard.vue'
|
||
|
||
const props = defineProps({
|
||
show: { type: Boolean, default: false },
|
||
presets: { type: Array, default: () => [] },
|
||
categories: { type: Array, default: () => [] },
|
||
})
|
||
|
||
const emit = defineEmits(['update:show', 'select'])
|
||
|
||
const search = ref('')
|
||
const activeCategory = ref('Все')
|
||
|
||
const filtered = computed(() => props.presets.filter((p) => {
|
||
const byCat = activeCategory.value === 'Все' || p.category === activeCategory.value || p.key === 'blank'
|
||
const byText = !search.value || p.label.toLowerCase().includes(search.value.toLowerCase())
|
||
return byCat && byText
|
||
}))
|
||
|
||
const pick = (preset) => {
|
||
emit('select', preset)
|
||
emit('update:show', false)
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<NModal
|
||
:show="show"
|
||
preset="card"
|
||
title="Шаблоны для отчёта"
|
||
class="max-w-[860px] h-[580px] relative"
|
||
:content-scrollable="true"
|
||
@update:show="emit('update:show', $event)"
|
||
>
|
||
<template #header-extra>
|
||
<NText depth="3" style="font-size: 13px;">Создавайте отчёт по шаблонам</NText>
|
||
</template>
|
||
|
||
<div class="picker relative flex gap-2 items-start">
|
||
<!-- Боковая панель с sticky -->
|
||
<div class="picker-side sticky top-0 shrink-0 align-start">
|
||
<NInput v-model:value="search" placeholder="Поиск" clearable size="small">
|
||
<template #prefix><TbSearch :size="14" /></template>
|
||
</NInput>
|
||
<div class="cat-list">
|
||
<div
|
||
v-for="cat in categories"
|
||
:key="cat"
|
||
class="cat-item"
|
||
:class="{ active: cat === activeCategory }"
|
||
@click="activeCategory = cat"
|
||
>
|
||
{{ cat }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Контент справа -->
|
||
<div style="flex: 1; min-width: 0;">
|
||
<NGrid responsive="screen" cols="2 s:3" :x-gap="12" :y-gap="12" class="pt-0.5">
|
||
<NGi v-for="preset in filtered" :key="preset.key">
|
||
<PresetCard :preset="preset" @click="pick(preset)" />
|
||
</NGi>
|
||
</NGrid>
|
||
</div>
|
||
</div>
|
||
</NModal>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.picker {
|
||
display: flex;
|
||
gap: 16px;
|
||
}
|
||
.picker-side {
|
||
width: 180px;
|
||
flex-shrink: 0;
|
||
}
|
||
.cat-list {
|
||
margin-top: 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.cat-item {
|
||
padding: 7px 10px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
}
|
||
.cat-item:hover {
|
||
background: color-mix(in srgb, var(--primary-color) 8%, transparent);
|
||
}
|
||
.cat-item.active {
|
||
background: color-mix(in srgb, var(--primary-color) 14%, transparent);
|
||
color: var(--primary-color);
|
||
font-weight: 600;
|
||
}
|
||
.picker-main {
|
||
flex: 1;
|
||
}
|
||
</style>
|