автоподстановка homepage

This commit is contained in:
Archibald 2025-06-09 21:45:25 +05:00
parent 7e6320aa9e
commit e6cd2049e0
4 changed files with 93 additions and 35 deletions

View File

@ -4,7 +4,7 @@ from typing import Optional, Any, Coroutine
import aiofiles
from fastapi import HTTPException, status, UploadFile
from magic import magic
from magic import Magic
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.responses import FileResponse
from werkzeug.utils import secure_filename
@ -152,7 +152,7 @@ class TeamsService:
@staticmethod
def validate_file_type(file: UploadFile):
mime = magic.Magic(mime=True)
mime = Magic(mime=True)
file_type = mime.from_buffer(file.file.read(1024))
file.file.seek(0)

View File

@ -4,7 +4,7 @@ import CONFIG from '@/core/config.js';
const downloadProjectFile = async (fileId) => {
try {
const response = await axios.get(
`${CONFIG.BASE_URL}/project_files/${fileId}/download`,
`${CONFIG.BASE_URL}/project_files/${fileId}/download/`,
{
responseType: 'blob',
withCredentials: true,

View File

@ -9,7 +9,7 @@ const uploadProjectFile = async (projectId, file) => {
formData.append("file", file);
const response = await axios.post(
`${CONFIG.BASE_URL}/project_files/projects/${projectId}/upload`,
`${CONFIG.BASE_URL}/project_files/projects/${projectId}/upload/`,
formData,
{
headers: {
@ -36,4 +36,4 @@ const uploadProjectFile = async (projectId, file) => {
}
};
export default uploadProjectFile;
export default uploadProjectFile;

View File

@ -2,13 +2,13 @@
<q-page class="home-page bg-violet-strong q-pa-md">
<div class="flex justify-center q-mb-md">
<q-avatar size="140px" class="team-logo shadow-12">
<q-avatar v-if="teamLogo" size="140px" class="team-logo shadow-12">
<img :src="teamLogo" alt="Логотип команды"/>
</q-avatar>
</div>
<div class="flex justify-center q-mb-xl">
<q-card class="team-name-card">
<q-card v-if="teamName" class="team-name-card">
<q-card-section class="text-h4 text-center text-indigo-10 q-pa-md">
{{ teamName }}
</q-card-section>
@ -122,13 +122,16 @@
</template>
<script setup>
import {ref, computed, onMounted} from 'vue'
import {useRouter} from 'vue-router'
import {Ripple, Notify} from 'quasar'
import axios from "axios";
import CONFIG from "@/core/config.js";
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { Ripple, Notify } from 'quasar'
import axios from 'axios'
import CONFIG from '@/core/config.js'
import fetchTeams from '@/api/teams/getTeams.js'
import fetchProfiles from '@/api/profiles/getProfiles.js'
import fetchContests from '@/api/contests/getContests.js'
defineExpose({directives: {ripple: Ripple}})
defineExpose({ directives: { ripple: Ripple } })
const router = useRouter()
@ -151,32 +154,21 @@ const handleAuthAction = () => {
}
// --- Данные команды ---
const teamLogo = ref('https://cdn.quasar.dev/logo-v2/svg/logo.svg')
const teamName = ref('Digital Dream Team')
const teamLogo = ref('')
const teamName = ref('')
// --- Участники ---
const members = ref([
{id: 1, name: 'Иван Иванов', role: 'Team Lead', avatar: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'},
{id: 2, name: 'Мария Петрова', role: 'Frontend', avatar: 'https://randomuser.me/api/portraits/women/44.jpg'},
{id: 3, name: 'Алексей Смирнов', role: 'Backend', avatar: 'https://randomuser.me/api/portraits/men/65.jpg'},
{id: 4, name: 'Анна Кузнецова', role: 'Designer', avatar: 'https://randomuser.me/api/portraits/women/56.jpg'},
{id: 5, name: 'Дмитрий Орлов', role: 'QA', avatar: 'https://randomuser.me/api/portraits/men/78.jpg'},
])
const members = ref([])
// --- Конкурсы ---
const contests = ref([
{id: 1, title: 'Hackathon 2024', description: 'Ежегодный хакатон для стартапов'},
{id: 2, title: 'CodeFest', description: 'Соревнование по программированию'},
{id: 3, title: 'UI/UX Challenge', description: 'Конкурс дизайна интерфейсов'},
{id: 4, title: 'AI Innovation', description: 'Инициатива в области искусственного интеллекта'},
])
const contests = ref([])
// --- Активность ---
const activityData = ref([]);
const dayHeight = 14;
const squareSize = ref(12);
// Подписи месяцев (с июня 2024 по май 2025, чтобы соответствовать текущему году)
// Подписи месяцев (с июня 2024 по май 2025)
const monthLabels = ['июн.', 'июл.', 'авг.', 'сент.', 'окт.', 'нояб.', 'дек.', 'янв.', 'февр.', 'март', 'апр.', 'май'];
// Дни недели (пн, ср, пт, как в Gitea)
@ -186,7 +178,9 @@ const weekDays = ['пн', 'ср', 'пт'];
const activityGrid = computed(() => {
const weeks = [];
let week = [];
const firstDay = new Date();
const firstDay
= new Date();
firstDay.setDate(firstDay.getDate() - 364); // Год назад от текущей даты
const dayOfWeek = firstDay.getDay();
const offset = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Смещение для выравнивания по понедельнику
@ -228,7 +222,71 @@ function getMonthMargin(idx) {
return weekIndex * (squareSize.value + 4); // 4 = margin (2px + 2px)
}
// Загрузка активности из API
// Загрузка данных команды
async function loadTeamData() {
try {
const teams = await fetchTeams();
const activeTeam = teams.find(team => team.is_active === true);
if (activeTeam) {
teamName.value = activeTeam.name;
teamLogo.value = activeTeam.logo;
} else {
Notify.create({
type: 'warning',
message: 'Активная команда не найдена',
icon: 'warning',
});
}
} catch (error) {
console.error('Ошибка загрузки данных команды:', error);
Notify.create({
type: 'negative',
message: 'Ошибка загрузки данных команды',
icon: 'error',
});
}
}
// Загрузка участников
async function loadMembers() {
try {
const profiles = await fetchProfiles();
members.value = profiles.map(profile => ({
id: profile.id,
name: profile.name || 'Без имени',
role: profile.role || 'Участник',
avatar: profile.avatar || 'https://randomuser.me/api/portraits/men/1.jpg',
}));
} catch (error) {
console.error('Ошибка загрузки участников:', error);
Notify.create({
type: 'negative',
message: error.message || 'Ошибка загрузки участников',
icon: 'error',
});
}
}
// Загрузка конкурсов
async function loadContests() {
try {
const fetchedContests = await fetchContests();
contests.value = fetchedContests.map(contest => ({
id: contest.id,
title: contest.title || 'Без названия',
description: contest.description || 'Описание отсутствует',
}));
} catch (error) {
console.error('Ошибка загрузки конкурсов:', error);
Notify.create({
type: 'negative',
message: error.message || 'Ошибка загрузки конкурсов',
icon: 'error',
});
}
}
// Загрузка активности
const username = 'archibald';
async function loadActivity() {
@ -249,7 +307,7 @@ async function loadActivity() {
date.setDate(startDate.getDate() + i);
const dateStr = date.toISOString().slice(0, 10);
const count = dataMap.get(dateStr) || 0;
activityData.value.push({date: dateStr, count});
activityData.value.push({ date: dateStr, count });
}
} catch (error) {
console.error('Ошибка загрузки активности:', error);
@ -268,13 +326,13 @@ async function loadActivity() {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
const dateStr = date.toISOString().slice(0, 10);
activityData.value.push({date: dateStr, count: 0});
activityData.value.push({ date: dateStr, count: 0 });
}
}
}
onMounted(() => {
loadActivity();
onMounted(async () => {
await Promise.all([loadTeamData(), loadMembers(), loadContests(), loadActivity()]);
});
// Масштабирование