feat: подготовил дизайн (изменения из другого репозитория)
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 5s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 8s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 3s

This commit is contained in:
2026-05-08 01:06:22 +03:00
parent 655ab1b5c5
commit 047611fd24
54 changed files with 4497 additions and 28 deletions
@@ -0,0 +1,124 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import GlassCard from '@/components/ui/GlassCard.vue'
import DataTable from '@/components/ui/DataTable.vue'
type TabKey = 'lectures' | 'courses' | 'rooms' | 'tags'
type TabConfig = {
title: string
columns: Array<{ key: string; label: string; align?: string }>
rows: Record<string, any>[]
}
const activeTab = ref<TabKey>('lectures')
const tabConfig: Record<TabKey, TabConfig> = {
lectures: {
title: 'Лекции',
columns: [
{ key: 'title', label: 'Название' },
{ key: 'teacher', label: 'Преподаватель' },
{ key: 'format', label: 'Формат' },
{ key: 'status', label: 'Синхронизация', align: 'center' },
],
rows: [
{ id: '1', title: 'Введение в нейронные сети', teacher: 'Волков М.С.', format: 'Офлайн', status: 'Синхронизировано' },
{ id: '2', title: 'Квантовые вычисления', teacher: 'Петров А.И.', format: 'Офлайн', status: 'Синхронизировано' },
{ id: '3', title: 'Философия цифровой эпохи', teacher: 'Дмитриев К.О.', format: 'Онлайн', status: 'Ошибка' },
],
},
courses: {
title: 'Курсы',
columns: [
{ key: 'title', label: 'Курс' },
{ key: 'institute', label: 'Институт' },
{ key: 'tags', label: 'Теги' },
],
rows: [
{ id: '1', title: 'Машинное обучение', institute: 'ИКТИБ', tags: '#ML #ИИ #Python' },
{ id: '2', title: 'Цифровая этика', institute: 'ИФиСН', tags: '#философия #этика' },
],
},
rooms: {
title: 'Аудитории',
columns: [
{ key: 'building', label: 'Корпус' },
{ key: 'room', label: 'Аудитория' },
{ key: 'capacity', label: 'Вместимость', align: 'center' },
],
rows: [
{ id: '1', building: 'ИКТИБ', room: '305', capacity: 30 },
{ id: '2', building: 'ИФиМКН', room: '201', capacity: 25 },
],
},
tags: {
title: 'Теги',
columns: [
{ key: 'tag', label: 'Тег' },
{ key: 'category', label: 'Категория' },
{ key: 'linked', label: 'Привязки', align: 'center' },
],
rows: [
{ id: '1', tag: '#ML', category: 'Data Science', linked: 12 },
{ id: '2', tag: '#философия', category: 'Гуманитарные', linked: 6 },
],
},
}
const current = computed(() => tabConfig[activeTab.value])
</script>
<template>
<div class="admin-lectures page-content">
<div class="header">
<h1 class="page-title">Управление лекциями и справочниками</h1>
<button class="btn-primary">Создать запись</button>
</div>
<div class="tabs">
<button :class="{ active: activeTab === 'lectures' }" @click="activeTab = 'lectures'">Лекции</button>
<button :class="{ active: activeTab === 'courses' }" @click="activeTab = 'courses'">Курсы</button>
<button :class="{ active: activeTab === 'rooms' }" @click="activeTab = 'rooms'">Аудитории</button>
<button :class="{ active: activeTab === 'tags' }" @click="activeTab = 'tags'">Теги</button>
</div>
<div class="grid">
<GlassCard>
<div class="section-title">{{ current.title }}</div>
<DataTable :columns="current.columns" :rows="current.rows" />
</GlassCard>
<GlassCard>
<div class="section-title">Создать / редактировать</div>
<form class="form">
<label>Название</label>
<input class="glass-input" placeholder="Введите название" />
<label>Описание</label>
<textarea rows="4" placeholder="Описание записи"></textarea>
<label>Статус синхронизации</label>
<select class="glass-input">
<option>Синхронизировано</option>
<option>Ожидает</option>
<option>Ошибка</option>
</select>
<div class="form-actions">
<button class="btn-primary" type="button">Сохранить</button>
<button class="btn-secondary" type="button">Отменить</button>
</div>
</form>
</GlassCard>
</div>
</div>
</template>
<style scoped>
.admin-lectures { display: flex; flex-direction: column; gap: 16px; }
.header { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.tabs { display: inline-flex; border: 1px solid var(--color-border-glass); border-radius: 12px; overflow: hidden; }
.tabs button { background: rgba(255,255,255,0.7); border: none; padding: 8px 18px; font-size: 13px; cursor: pointer; color: var(--color-text-secondary); }
.tabs button.active { background: rgba(34,197,94,0.18); color: var(--color-primary-dark); font-weight: 600; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
.form { display: flex; flex-direction: column; gap: 10px; }
textarea { padding: 10px; border-radius: var(--radius-sm); border: 1px solid var(--color-border-glass); background: rgba(255,255,255,0.8); }
.form-actions { display: flex; gap: 10px; }
</style>