This commit is contained in:
root
2025-09-15 09:31:51 +05:00
parent 32e9ce8267
commit 13b613e191
18 changed files with 4048 additions and 3 deletions
+220
View File
@@ -0,0 +1,220 @@
<template>
<Card class="mb-4">
<template #title>
<div class="flex align-items-center">
<i class="pi pi-filter mr-2"></i>
Фильтры
</div>
</template>
<template #content>
<div class="grid">
<!-- Фильтр по источнику -->
<div class="col-12 md:col-4">
<div class="field">
<label for="sourceFilter" class="font-medium block mb-2">Источник</label>
<Dropdown id="sourceFilter" v-model="localFilters.sourceName" :options="sourceOptions" optionLabel="label" optionValue="value" placeholder="Все источники" :loading="loading" showClear class="w-full" />
</div>
</div>
<!-- Фильтр по дате начала -->
<div class="col-12 md:col-4">
<div class="field">
<label for="startDate" class="font-medium block mb-2">Дата от</label>
<Calendar id="startDate" v-model="localFilters.startDate" placeholder="Выберите дату" dateFormat="yy-mm-dd" showIcon class="w-full" />
</div>
</div>
<!-- Фильтр по дате окончания -->
<div class="col-12 md:col-4">
<div class="field">
<label for="endDate" class="font-medium block mb-2">Дата до</label>
<Calendar id="endDate" v-model="localFilters.endDate" placeholder="Выберите дату" dateFormat="yy-mm-dd" showIcon class="w-full" />
</div>
</div>
<!-- Сортировка -->
<div class="col-12 md:col-6">
<div class="field">
<label for="sortField" class="font-medium block mb-2">Сортировка по</label>
<Dropdown id="sortField" v-model="sortField" :options="sortFieldOptions" optionLabel="label" optionValue="value" placeholder="Выберите поле" class="w-full" />
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<label for="sortOrder" class="font-medium block mb-2">Порядок</label>
<Dropdown id="sortOrder" v-model="sortOrder" :options="sortOrderOptions" optionLabel="label" optionValue="value" placeholder="Выберите порядок" class="w-full" />
</div>
</div>
<!-- Размер страницы -->
<div class="col-12 md:col-6">
<div class="field">
<label for="pageSize" class="font-medium block mb-2">Новостей на странице</label>
<Dropdown id="pageSize" v-model="localFilters.size" :options="pageSizeOptions" optionLabel="label" optionValue="value" placeholder="Выберите размер" class="w-full" />
</div>
</div>
<!-- Быстрые фильтры -->
<div class="col-12 md:col-6">
<div class="field">
<label class="font-medium block mb-2">Быстрые фильтры</label>
<div class="flex flex-wrap gap-2">
<Button label="Сегодня" size="small" severity="secondary" @click="setTodayFilter" />
<Button label="Неделя" size="small" severity="secondary" @click="setWeekFilter" />
<Button label="Месяц" size="small" severity="secondary" @click="setMonthFilter" />
<Button label="Очистить" size="small" severity="danger" @click="clearFilters" />
</div>
</div>
</div>
</div>
<!-- Кнопки действий -->
<div class="flex justify-content-end gap-2 mt-3">
<Button label="Сбросить" icon="pi pi-times" severity="secondary" @click="resetFilters" />
<Button label="Применить" icon="pi pi-check" @click="applyFilters" :loading="loading" />
</div>
</template>
</Card>
</template>
<script setup>
import Button from 'primevue/button';
import Calendar from 'primevue/calendar';
import Card from 'primevue/card';
import Dropdown from 'primevue/dropdown';
import { computed, reactive, ref, watch } from 'vue';
const props = defineProps({
filters: {
type: Object,
required: true
},
sources: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:filters', 'filter-change']);
// Локальные фильтры
const localFilters = reactive({
sourceName: null,
startDate: null,
endDate: null,
size: 20
});
// Сортировка
const sortField = ref('publishedAt');
const sortOrder = ref('desc');
// Опции для выпадающих списков
const sourceOptions = computed(() => [
{ label: 'Все источники', value: null },
...props.sources.map((source) => ({
label: source,
value: source
}))
]);
const sortFieldOptions = [
{ label: 'Дата публикации', value: 'publishedAt' },
{ label: 'Дата добавления', value: 'addedAt' },
{ label: 'Заголовок', value: 'title' },
{ label: 'Источник', value: 'sourceName' }
];
const sortOrderOptions = [
{ label: 'По убыванию', value: 'desc' },
{ label: 'По возрастанию', value: 'asc' }
];
const pageSizeOptions = [
{ label: '10 новостей', value: 10 },
{ label: '20 новостей', value: 20 },
{ label: '50 новостей', value: 50 },
{ label: '100 новостей', value: 100 }
];
// Методы
const applyFilters = () => {
const newFilters = {
...localFilters,
sort: `${sortField.value},${sortOrder.value}`,
page: 0
};
emit('update:filters', newFilters);
emit('filter-change');
};
const resetFilters = () => {
localFilters.sourceName = null;
localFilters.startDate = null;
localFilters.endDate = null;
localFilters.size = 20;
sortField.value = 'publishedAt';
sortOrder.value = 'desc';
applyFilters();
};
const clearFilters = () => {
resetFilters();
};
const setTodayFilter = () => {
const today = new Date();
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
const endOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59);
localFilters.startDate = startOfDay;
localFilters.endDate = endOfDay;
applyFilters();
};
const setWeekFilter = () => {
const today = new Date();
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
localFilters.startDate = weekAgo;
localFilters.endDate = today;
applyFilters();
};
const setMonthFilter = () => {
const today = new Date();
const monthAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000);
localFilters.startDate = monthAgo;
localFilters.endDate = today;
applyFilters();
};
// Инициализация из props
const initializeFromProps = () => {
localFilters.sourceName = props.filters.sourceName;
localFilters.startDate = props.filters.startDate ? new Date(props.filters.startDate) : null;
localFilters.endDate = props.filters.endDate ? new Date(props.filters.endDate) : null;
localFilters.size = props.filters.size || 20;
const [field, order] = (props.filters.sort || 'publishedAt,desc').split(',');
sortField.value = field;
sortOrder.value = order;
};
// Отслеживание изменений props
watch(() => props.filters, initializeFromProps, { immediate: true, deep: true });
</script>
<style scoped>
.field {
margin-bottom: 1rem;
}
</style>