news
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user