feat: добавил кабинеты преподавателя и администратора
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 8s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 38s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 18s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 6s

This commit is contained in:
2026-05-11 01:58:09 +03:00
parent 779b6aba77
commit 610c15c9fd
11 changed files with 399 additions and 90 deletions
+59 -12
View File
@@ -1,11 +1,17 @@
<script setup lang="ts">
import { ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { usersApi } from '@/api'
import type { UserDto } from '@/api/types'
import GlassCard from '@/components/ui/GlassCard.vue'
import DataTable from '@/components/ui/DataTable.vue'
import EmptyState from '@/components/ui/EmptyState.vue'
const search = ref('')
const roleFilter = ref('Все роли')
const instituteFilter = ref('Все институты')
const users = ref<UserDto[]>([])
const loading = ref(false)
const error = ref('')
const columns = [
{ key: 'name', label: 'Имя' },
@@ -17,11 +23,51 @@ const columns = [
{ key: 'actions', label: 'Действия', align: 'right' },
]
const rows = [
{ id: '1', name: 'Алексей Морозов', email: 'a.morozov@sfedu.ru', role: 'Студент', institute: 'ИКТИБ', activity: 'Высокая', created: '12.03.2024' },
{ id: '2', name: 'Елена Смирнова', email: 'e.smirnova@sfedu.ru', role: 'Преподаватель', institute: 'АГиС', activity: 'Средняя', created: '05.02.2023' },
{ id: '3', name: 'Виктор Алексеев', email: 'admin@sfedu.ru', role: 'Администратор', institute: 'ЮФУ', activity: 'Высокая', created: '01.09.2022' },
]
const roleLabels = { Student: 'Студент', Teacher: 'Преподаватель', Admin: 'Администратор' } as const
const roleApi = { Студент: 'Student', Преподаватель: 'Teacher', Администратор: 'Admin' } as const
const rows = computed(() =>
users.value.map(user => ({
id: user.id,
name: user.displayName || user.email,
email: user.email,
role: roleLabels[user.role],
apiRole: user.role,
institute: 'ЮФУ',
activity: user.isActive ? 'Активен' : 'Заблокирован',
isActive: user.isActive,
created: new Date(user.createdAt).toLocaleDateString('ru-RU'),
})),
)
async function fetchUsers() {
loading.value = true
error.value = ''
try {
users.value = await usersApi.list({
Search: search.value || undefined,
Role: roleFilter.value === 'Все роли' ? undefined : roleApi[roleFilter.value as keyof typeof roleApi],
PageSize: 100,
})
} catch (err) {
error.value = err instanceof Error ? err.message : 'Не удалось загрузить пользователей.'
} finally {
loading.value = false
}
}
async function toggleActive(row: Record<string, any>) {
await usersApi.setActive(row.id, !row.isActive)
await fetchUsers()
}
async function promoteRole(row: Record<string, any>) {
const next = row.apiRole === 'Student' ? 'Teacher' : row.apiRole === 'Teacher' ? 'Admin' : 'Student'
await usersApi.setRole(row.id, next)
await fetchUsers()
}
onMounted(fetchUsers)
</script>
<template>
@@ -34,7 +80,7 @@ const rows = [
<GlassCard>
<div class="filters">
<input v-model="search" class="glass-input" placeholder="Поиск по имени или email" />
<select v-model="roleFilter" class="glass-input">
<select v-model="roleFilter" class="glass-input" @change="fetchUsers">
<option>Все роли</option>
<option>Студент</option>
<option>Преподаватель</option>
@@ -49,18 +95,19 @@ const rows = [
</select>
</div>
<EmptyState v-if="error" title="Не удалось загрузить пользователей" :subtitle="error" />
<EmptyState v-else-if="!rows.length && !loading" title="Пользователей не найдено" subtitle="Попробуйте изменить фильтры." />
<DataTable :columns="columns" :rows="rows">
<template #role="{ value }">
<span :class="value === 'Студент' ? 'badge badge-green' : value === 'Преподаватель' ? 'badge badge-blue' : 'badge badge-purple'">{{ value }}</span>
</template>
<template #activity="{ value }">
<span class="badge" :class="value === 'Высокая' ? 'badge-green' : 'badge-orange'">{{ value }}</span>
<span class="badge" :class="value === 'Активен' ? 'badge-green' : 'badge-orange'">{{ value }}</span>
</template>
<template #actions>
<template #actions="{ row }">
<div class="actions">
<button class="btn-ghost">Назначить роль</button>
<button class="btn-ghost">Заблокировать</button>
<button class="btn-ghost">Профиль</button>
<button class="btn-ghost" @click="promoteRole(row)">Назначить роль</button>
<button class="btn-ghost" @click="toggleActive(row)">{{ row.isActive ? 'Заблокировать' : 'Активировать' }}</button>
</div>
</template>
</DataTable>