rick-and-morty-vue3
195 строк · 5.2 Кб
1<template>
2<!-- Общая информация о количестве персонажей, локаций и эпизодов -->
3<section class="info">
4<p>Персонажи: {{ info.countCharacters }}</p>
5<p>Локации: {{ info.countLocations }}</p>
6<p>Эпизоды: {{ info.countEpisodes }}</p>
7</section>
8
9<!-- Поля фильтрации по имени, статусу и полу -->
10<section class="filter">
11<Input
12v-model="filters.name"
13placeholder="Искать по имени..."
14@keydown.enter="handleSearch"
15/>
16<Select
17v-model="filters.status"
18:options="statuses"
19placeholder="Выбрать статус"
20@keydown.enter="handleSearch"
21/>
22<Select
23v-model="filters.gender"
24:options="genders"
25placeholder="Выбрать пол"
26@keydown.enter="handleSearch"
27/>
28<Button @click="handleSearch" :disabled="!isSearchEnabled">Поиск</Button>
29</section>
30
31<!-- Отображение списка карточек персонажей -->
32<section class="section">
33<div class="card-list">
34<Card
35v-for="character in paginatedCharacters"
36:key="character.id"
37:character="character"
38/>
39</div>
40<Loader v-if="loading" />
41</section>
42
43<!-- Элемент выбора количества карточек на страницу -->
44<section class="section">
45<ItemsPerPage v-model="cardsPerPage" />
46</section>
47
48<!-- Пагинация -->
49<section class="section">
50<Pagination
51:currentPage="currentPage"
52:hasMoreCharacters="hasMoreCharacters"
53@prevPage="prevPage"
54@nextPage="nextPage"
55/>
56</section>
57
58<!-- Кнопка "Вверх" -->
59<ScrollToTop />
60</template>
61
62<script setup>
63import { onMounted, ref, computed } from "vue";
64import { useCharacters } from "@/services/useCharacters";
65import ItemsPerPage from "@/components/ItemsPerPage.vue";
66import Pagination from "@/components/Pagination.vue";
67import Input from "@/components/Input.vue";
68import Select from "@/components/Select.vue";
69import Button from "@/components/Button.vue";
70import Card from "@/components/Card.vue";
71import Loader from "@/components/Loader.vue";
72import ScrollToTop from "@/components/ScrollToTop.vue";
73
74// Варианты выбора фильтров на русском языке
75const statusMap = {
76Alive: "Живой",
77Dead: "Мёртвый",
78Unknown: "Неизвестно",
79};
80
81const genderMap = {
82Male: "Мужской",
83Female: "Женский",
84Genderless: "Бесполый",
85Unknown: "Неизвестно",
86};
87
88// Опции Select, которые будут отображаться на русском
89const statuses = Object.values(statusMap);
90const genders = Object.values(genderMap);
91
92// Фильтры для поиска
93const filters = ref({
94name: "",
95status: "",
96gender: "",
97});
98
99// Параметры пагинации
100const cardsPerPage = ref(10);
101const currentPage = ref(1);
102
103// Получение данных персонажей и информации
104const { characters, loading, loadCharacters, resetCharacters, info, loadInfo } = useCharacters();
105
106// Отображение карточек на текущей странице
107const paginatedCharacters = computed(() => {
108const start = (currentPage.value - 1) * cardsPerPage.value;
109const end = start + cardsPerPage.value;
110return characters.value.slice(start, end);
111});
112
113const hasMoreCharacters = computed(() => {
114return characters.value.length > currentPage.value * cardsPerPage.value;
115});
116
117// Функция поиска персонажей по фильтрам
118const handleSearch = () => {
119// Преобразование выбранных фильтров на русском обратно в английский для API
120const translatedFilters = {
121name: filters.value.name,
122status: Object.keys(statusMap).find(
123(key) => statusMap[key] === filters.value.status
124),
125gender: Object.keys(genderMap).find(
126(key) => genderMap[key] === filters.value.gender
127),
128};
129
130resetCharacters();
131loadCharacters(translatedFilters);
132};
133
134// Переход по страницам
135const prevPage = () => {
136if (currentPage.value > 1) {
137currentPage.value--;
138}
139};
140
141const nextPage = () => {
142if (hasMoreCharacters.value) {
143currentPage.value++;
144}
145};
146
147// Проверка, включена ли кнопка поиска
148const isSearchEnabled = computed(() => {
149return filters.value.name || filters.value.status || filters.value.gender;
150});
151
152// Начальная загрузка данных
153onMounted(async () => {
154await loadCharacters(filters.value);
155await loadInfo();
156});
157
158</script>
159
160<style lang="scss" scoped>
161.section {
162display: flex;
163justify-content: center;
164margin: 0;
165padding: 1rem;
166}
167
168.card-list {
169display: flex;
170justify-content: center;
171flex-wrap: wrap;
172gap: 8px;
173}
174
175.filter {
176width: auto;
177display: flex;
178justify-content: center;
179padding: 5px 20px;
180gap: 1rem;
181
182@media (max-width: 480px) {
183flex-direction: column;
184padding: 0px 20px;
185}
186}
187
188.info {
189display: flex;
190justify-content: center;
191gap: 10px;
192text-align: center;
193color: rgb(158, 158, 158);
194}
195</style>
196