generated from profcomff/vuets-template
-
Notifications
You must be signed in to change notification settings - Fork 1
Компактное отображение списка преподов Дубинушки #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
9410881
main page ->сокращенный формат
Dmatrushka19 71c0799
рабочий вариант, но нужно исправлять (есть скролл и звездочки), стран…
Dmatrushka19 866b8e1
Два режима: компактный и полный. При возврате из карточки обратно, ре…
Dmatrushka19 ffcc9c5
Элемент с разметкой для таблицы компактного режима.
Dmatrushka19 02b0b4c
Добавил оглавление таблицы
Dmatrushka19 b052004
не отображаю Null-предметы
Dmatrushka19 fdecca8
# -> № в заголовке таблицы
Dmatrushka19 ea80a36
Убрал ошибки по ESLint: использовал слоты без деструктуризации, но с …
Dmatrushka19 073839f
починил отображение плашки "еще n" в графе предметов
Dmatrushka19 442a538
обновил название элемента
Dmatrushka19 6c0db48
Добавил стор для преподов и утилс для функции вычесления цвета оценки
Dmatrushka19 0945746
store, который хранитинфу о преподах
Dmatrushka19 8d60319
удалил лишние комменты
Dmatrushka19 a807fc3
убрал elevation, дитруктурировал, вынес отдельные условия + убрал час…
Dmatrushka19 1c2e050
поддержка изменений в MainPage
Dmatrushka19 836c05a
убрал комменты
Dmatrushka19 1670d9f
добавил стор, который хранит состояние списка (таблица или с полный вид)
Dmatrushka19 f45d9d4
использование стора, который хранит состояние списка преподов
Dmatrushka19 1fcf818
ref to computed
BatuevIO 141bdc9
support handle change
BatuevIO f5146bd
support other handle
BatuevIO 78bf718
Merge branch 'main' into Compact-view-of-lecturer-search-screen
BatuevIO File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| <template> | ||
| <v-data-table | ||
| :headers="headers" | ||
| :items="tableItems" | ||
| hide-default-footer | ||
| disable-sort | ||
| class="lecturer-table" | ||
| @click:row="handleRowClick" | ||
| > | ||
| <template #[`item.rating`]="{ index }"> | ||
| {{ ratings[index] }} | ||
| </template> | ||
|
|
||
| <template | ||
| #[`item.fullName`]="{ | ||
| item: { | ||
| raw: { last_name, first_name, middle_name }, | ||
| }, | ||
| }" | ||
| > | ||
| <strong>{{ last_name }}</strong> {{ first_name }} {{ middle_name }} | ||
| </template> | ||
|
|
||
| <template #[`item.subjects`]="{ item }"> | ||
| <v-chip-group v-if="getFilteredSubjects(item.raw.subjects).length > 0" class="my-1"> | ||
| <v-chip | ||
| v-for="(subject, idx) in getFilteredSubjects(item.raw.subjects).slice(0, 2)" | ||
| :key="idx" | ||
| size="small" | ||
| readonly | ||
| :ripple="false" | ||
| class="mr-1" | ||
| > | ||
| {{ subject }} | ||
| </v-chip> | ||
| <v-chip v-if="isSubjectOverflow(item.raw.subjects)" size="small" readonly :ripple="false"> | ||
| еще {{ remainingSubjectsCount(item.raw.subjects) }} | ||
| </v-chip> | ||
| </v-chip-group> | ||
| </template> | ||
|
|
||
| <template #[`item.comments`]="{ item }"> | ||
| {{ item.raw.comments?.length || '—' }} | ||
| </template> | ||
|
|
||
| <template #[`item.mark_general`]="{ item }"> | ||
| <v-avatar size="30" :color="getMarkColor(item.raw.mark_general)" class="white--text"> | ||
| {{ formatMark(item.raw.mark_general) }} | ||
| </v-avatar> | ||
| </template> | ||
| </v-data-table> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import { computed } from 'vue'; | ||
| import { Lecturer } from '@/models'; | ||
| import { formatMark, getMarkColor } from '@/utils/marks'; // Импорт из нового файла | ||
|
|
||
| interface TableItem { | ||
| raw: Lecturer; | ||
| } | ||
|
|
||
| interface CustomDataTableHeader { | ||
| title: string; | ||
| key: string; | ||
| width?: string; | ||
| sortable?: boolean; | ||
| align?: 'start' | 'center' | 'end'; | ||
| } | ||
|
|
||
| const props = defineProps({ | ||
| lecturers: { type: Array as () => Lecturer[], required: true }, | ||
| ratings: { type: Array as () => number[], required: true }, | ||
| }); | ||
|
|
||
| const emit = defineEmits(['lecturerClick']); | ||
|
|
||
| const headers: CustomDataTableHeader[] = [ | ||
| { title: '#', key: 'rating', width: '50px', sortable: false }, | ||
| { title: 'ФИО', key: 'fullName', sortable: false }, | ||
| { title: 'Предметы', key: 'subjects', sortable: false }, | ||
| { title: 'Отзывы', key: 'comments', align: 'center', sortable: false }, | ||
| { title: 'Оценка', key: 'mark_general', align: 'center', sortable: false }, | ||
| ]; | ||
|
|
||
| const tableItems = computed(() => { | ||
| if (!props.lecturers) return []; | ||
| return props.lecturers.map(lecturer => ({ | ||
| raw: lecturer, | ||
| })); | ||
| }); | ||
|
|
||
| function getFilteredSubjects(subjects: string[] | null | undefined): string[] { | ||
| if (!subjects) return []; | ||
| return subjects.filter((subject): subject is string => subject !== null); | ||
| } | ||
|
|
||
| // Условие для отображения плашки "еще N" | ||
| function isSubjectOverflow(subjects: string[] | null | undefined): boolean { | ||
| return getFilteredSubjects(subjects).length > 2; | ||
| } | ||
|
|
||
| // Расчет количества оставшихся предметов | ||
| function remainingSubjectsCount(subjects: string[] | null | undefined): number { | ||
| return getFilteredSubjects(subjects).length - 2; | ||
| } | ||
|
|
||
| function handleRowClick(event: Event, { item }: { item: TableItem }) { | ||
| emit('lecturerClick', item.raw.id); | ||
| } | ||
| </script> | ||
|
|
||
| <style scoped> | ||
| .lecturer-table { | ||
| cursor: pointer; | ||
| } | ||
|
|
||
| :deep(.lecturer-table thead th) { | ||
| font-weight: bold; | ||
| background-color: #f5f5f5; | ||
| } | ||
|
|
||
| :deep(.lecturer-table tbody tr:hover) { | ||
| background-color: rgb(0 0 0 / 4%); | ||
| } | ||
| </style> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,139 +1,137 @@ | ||
| <script async setup lang="ts"> | ||
| import { ref, computed, watch } from 'vue'; | ||
| import { storeToRefs } from 'pinia'; | ||
| import { router } from '@/router'; | ||
| import { ref, Ref } from 'vue'; | ||
| import apiClient from '@/api'; | ||
| import { useProfileStore } from '@/store'; | ||
| import Placeholder from '@/assets/profile_image_placeholder.webp'; | ||
| import { useSearchStore } from '@/store/searchStore'; | ||
| import { useLecturerStore } from '@/store/lecturerStore'; | ||
| import { useMainPageStateStore } from '@/store/mainPageStateStore'; // Обновленный импорт | ||
| import { Order, OrderFromText, Subject } from '@/models'; | ||
|
|
||
| import TheSearchBar from '@/components/TheSearchBar.vue'; | ||
| import TheLecturerSearchCard from '@/components/TheLecturerSearchCard.vue'; | ||
| import { Lecturer, Order, OrderFromText, Subject } from '@/models'; | ||
| import { getPhoto } from '@/utils'; | ||
| import { useSearchStore } from '@/store/searchStore'; | ||
| import TheLecturerSearchTable from '@/components/TheLecturerSearchTable.vue'; | ||
|
|
||
| // const | ||
| const profileStore = useProfileStore(); | ||
| const searchStore = useSearchStore(); | ||
| const userAdmin = ref<boolean>(false); | ||
| userAdmin.value = profileStore.isAdmin(); | ||
| const itemsPerPage = 10; | ||
| const lecturerStore = useLecturerStore(); | ||
| const mainPageStateStore = useMainPageStateStore(); // Обновленная инициализация | ||
|
|
||
| const { lecturers, lecturersPhotos, totalPages } = storeToRefs(lecturerStore); | ||
| const { isCompactView } = storeToRefs(mainPageStateStore); // Получение состояния из нового стора | ||
|
|
||
| // utils | ||
| const totalPages: Ref<number> = ref(1); | ||
| const userAdmin = computed(() => profileStore.isAdmin()); | ||
| const itemsPerPage = 10; | ||
|
|
||
| // search state | ||
| // Параметры поиска | ||
| const name = ref(searchStore.name); | ||
| const subject: Ref<Subject> = ref(searchStore.subject); | ||
| const subject = ref<Subject>(searchStore.subject); | ||
| const order = ref(searchStore.order || 'по релевантности'); | ||
| const orderValues = ref(OrderFromText[order.value as keyof typeof OrderFromText] as Order); | ||
| const ascending = ref(searchStore.ascending); | ||
| const page = ref(searchStore.page); | ||
|
|
||
| const lecturers: Ref<Lecturer[] | undefined> = ref(); | ||
| const lecturersPhotos = ref<string[]>(Array<string>(itemsPerPage)); | ||
|
|
||
| await loadLecturers(); | ||
|
|
||
| function toLecturerPage(id: number) { | ||
| searchStore.setParams(name.value, subject.value, order.value, ascending.value, page.value); | ||
| router.push({ path: 'lecturer', query: { lecturer_id: id } }); | ||
| } | ||
|
|
||
| async function loadLecturers() { | ||
| const offset = (page.value - 1) * itemsPerPage; | ||
| const res = await apiClient.GET('/rating/lecturer', { | ||
| params: { | ||
| query: { | ||
| limit: itemsPerPage, | ||
| name: name.value, | ||
| offset, | ||
| info: ['comments', 'mark'], | ||
| subject: subject.value, | ||
| order_by: orderValues.value, | ||
| asc_order: ascending.value, | ||
| }, | ||
| }, | ||
| // Вычисляем порядковые номера для компактного режима | ||
| const lecturerRatings = computed(() => { | ||
| if (!lecturers.value) return []; | ||
| return lecturers.value.map((_, idx) => (page.value - 1) * itemsPerPage + idx + 1); | ||
| }); | ||
|
|
||
| async function updateLecturersList() { | ||
| await lecturerStore.fetchLecturers({ | ||
| page: page.value, | ||
| itemsPerPage, | ||
| name: name.value, | ||
| subject: subject.value, | ||
| orderBy: OrderFromText[order.value as keyof typeof OrderFromText] as Order, | ||
| ascending: ascending.value, | ||
| }); | ||
| lecturers.value = res.data?.lecturers; | ||
| totalPages.value = res.data?.total ? Math.ceil(res.data?.total / itemsPerPage) : 1; | ||
| loadPhotos(); | ||
| } | ||
|
|
||
| function loadPhotos() { | ||
| lecturersPhotos.value = lecturers.value?.map(item => getPhoto(item.avatar_link)) ?? [Placeholder]; | ||
| } | ||
| // Загрузка при первой загрузке | ||
| await updateLecturersList(); | ||
|
|
||
| async function findLecturer() { | ||
| // Обработчики изменений | ||
| async function onSearchParamChange() { | ||
| page.value = 1; | ||
| await loadLecturers(); | ||
| await updateLecturersList(); | ||
| } | ||
|
|
||
| async function orderLecturers() { | ||
| page.value = 1; | ||
| orderValues.value = OrderFromText[order.value as keyof typeof OrderFromText] as Order; | ||
| await loadLecturers(); | ||
| } | ||
| watch(page, updateLecturersList); | ||
|
|
||
| async function filterLecturers() { | ||
| page.value = 1; | ||
| await loadLecturers(); | ||
| function toLecturerPage(id: number) { | ||
| searchStore.setParams(name.value, subject.value, order.value, ascending.value, page.value); | ||
| router.push({ path: 'lecturer', query: { lecturer_id: id } }); | ||
| } | ||
|
|
||
| async function changeAscOrder() { | ||
| page.value = 1; | ||
| ascending.value = !ascending.value; | ||
| await loadLecturers(); | ||
| function toggleViewMode() { | ||
| mainPageStateStore.toggleCompactView(); // Используем метод из нового стора | ||
| } | ||
| </script> | ||
|
|
||
| <template> | ||
| <v-container class="ma-0 py-2"> | ||
| <v-data-iterator :items="lecturers" :items-per-page="itemsPerPage"> | ||
| <template #header> | ||
| <TheSearchBar | ||
| v-model:search-query="name" | ||
| v-model:subject="subject" | ||
| v-model:order="order" | ||
| :is-admin="userAdmin" | ||
| :ascending="ascending" | ||
| :page="page" | ||
| @update:subject="filterLecturers" | ||
| @update:order="orderLecturers" | ||
| @update:search-query="findLecturer" | ||
| @changed-asc-desc="changeAscOrder" | ||
| /> | ||
| </template> | ||
|
|
||
| <template #default="{ items }"> | ||
| <TheLecturerSearchCard | ||
| v-for="(item, idx) in items" | ||
| :key="idx" | ||
| :lecturer="item.raw" | ||
| :photo="lecturersPhotos[idx]" | ||
| :rating="(page - 1) * itemsPerPage + idx + 1" | ||
| class="py-0" | ||
| variant="elevated" | ||
| @click="toLecturerPage(item.raw.id)" | ||
| /> | ||
| </template> | ||
|
|
||
| <template #no-data> | ||
| <div class="ma-2">Ничего не нашли :(</div> | ||
| </template> | ||
|
|
||
| <template #footer> | ||
| <div v-if="lecturers && totalPages > 1"> | ||
| <v-pagination | ||
| v-model="page" | ||
| active-color="primary" | ||
| <TheSearchBar | ||
| v-model:search-query="name" | ||
| v-model:subject="subject" | ||
| v-model:order="order" | ||
| :is-admin="userAdmin" | ||
| :ascending="ascending" | ||
| :page="page" | ||
| @update:subject="onSearchParamChange" | ||
| @update:order="onSearchParamChange" | ||
| @update:search-query="onSearchParamChange" | ||
| @changed-asc-desc=" | ||
| () => { | ||
| ascending = !ascending; | ||
| onSearchParamChange(); | ||
| } | ||
| " | ||
| /> | ||
|
|
||
| <div v-if="!isCompactView"> | ||
| <v-data-iterator :items="lecturers" :items-per-page="itemsPerPage"> | ||
| <template #default="{ items }"> | ||
| <TheLecturerSearchCard | ||
| v-for="(item, idx) in items" | ||
| :key="idx" | ||
| :lecturer="item.raw" | ||
| :photo="lecturersPhotos[idx]" | ||
| :rating="(page - 1) * itemsPerPage + idx + 1" | ||
| class="py-0" | ||
| variant="elevated" | ||
| :length="totalPages" | ||
| :total-visible="1" | ||
| :show-first-last-page="true" | ||
| ellipsis="" | ||
| @update:model-value="loadLecturers" | ||
| @click="toLecturerPage(item.raw.id)" | ||
| /> | ||
| </div> | ||
| </template> | ||
| </v-data-iterator> | ||
| </template> | ||
| <template #no-data> | ||
| <div class="ma-2">Ничего не нашли :(</div> | ||
| </template> | ||
| </v-data-iterator> | ||
| </div> | ||
|
|
||
| <div v-else> | ||
| <TheLecturerSearchTable | ||
| v-if="lecturers && lecturers.length > 0" | ||
| :lecturers="lecturers" | ||
| :ratings="lecturerRatings" | ||
| @lecturer-click="toLecturerPage" | ||
| /> | ||
| <div v-else class="ma-2">Ничего не нашли :(</div> | ||
| </div> | ||
|
|
||
| <div v-if="lecturers && totalPages > 1" class="d-flex align-center justify-center mt-4"> | ||
| <v-btn icon class="mr-2" :title="isCompactView ? 'Обычный вид' : 'Компактный вид'" @click="toggleViewMode"> | ||
| <v-icon>{{ isCompactView ? 'mdi-view-agenda' : 'mdi-table' }}</v-icon> | ||
| </v-btn> | ||
|
|
||
| <v-pagination | ||
| v-model="page" | ||
| active-color="primary" | ||
| variant="elevated" | ||
| :length="totalPages" | ||
| :total-visible="1" | ||
| :show-first-last-page="true" | ||
| ellipsis="" | ||
| /> | ||
| </div> | ||
| </v-container> | ||
| </template> | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тоже хорошо будет убрать в стор
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Это можно сделать не сейчас