сделал вкладку команд
This commit is contained in:
parent
ebf27d9ad0
commit
ac42a8da7f
23
WEB/src/api/teams/createTeam.js
Normal file
23
WEB/src/api/teams/createTeam.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import CONFIG from '@/core/config.js'
|
||||||
|
|
||||||
|
const createTeam = async (team) => {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('access_token') // или другой способ
|
||||||
|
const response = await axios.post(
|
||||||
|
`${CONFIG.BASE_URL}/teams`,
|
||||||
|
team,
|
||||||
|
{
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return response.data
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error.response?.data?.detail || error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createTeam
|
||||||
16
WEB/src/api/teams/deleteTeam.js
Normal file
16
WEB/src/api/teams/deleteTeam.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import CONFIG from '@/core/config.js'
|
||||||
|
|
||||||
|
const deleteTeam = async (teamId) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.delete(
|
||||||
|
`${CONFIG.BASE_URL}/teams/${teamId}`,
|
||||||
|
{ withCredentials: true }
|
||||||
|
)
|
||||||
|
return response.data
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error.response?.data?.detail || error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default deleteTeam
|
||||||
@ -3,11 +3,25 @@ import CONFIG from '@/core/config.js'
|
|||||||
|
|
||||||
const updateTeam = async (team) => {
|
const updateTeam = async (team) => {
|
||||||
try {
|
try {
|
||||||
|
const token = localStorage.getItem('access_token')
|
||||||
|
|
||||||
|
// Убираем id из тела запроса, он идет в URL
|
||||||
|
const { id, ...teamData } = team
|
||||||
|
|
||||||
|
console.log('Отправляем на сервер:', teamData)
|
||||||
|
|
||||||
const response = await axios.put(
|
const response = await axios.put(
|
||||||
`${CONFIG.BASE_URL}/teams/${team.id}`,
|
`${CONFIG.BASE_URL}/teams/${id}`,
|
||||||
team,
|
teamData,
|
||||||
{ withCredentials: true }
|
{
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.log('Ответ от сервера:', response.data)
|
||||||
return response.data
|
return response.data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error.response?.data?.detail || error.message)
|
throw new Error(error.response?.data?.detail || error.message)
|
||||||
|
|||||||
@ -24,7 +24,8 @@ import {
|
|||||||
QSeparator,
|
QSeparator,
|
||||||
QCardActions,
|
QCardActions,
|
||||||
QDialog,
|
QDialog,
|
||||||
QIcon
|
QIcon,
|
||||||
|
QSpace
|
||||||
} from 'quasar'
|
} from 'quasar'
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ app.use(Quasar, {
|
|||||||
QInput, QBtn, QForm, QCard, QCardSection,
|
QInput, QBtn, QForm, QCard, QCardSection,
|
||||||
QLayout, QPageContainer, QPage,
|
QLayout, QPageContainer, QPage,
|
||||||
QTabs, QTab, QTabPanels, QTabPanel, QHeader,QTable,
|
QTabs, QTab, QTabPanels, QTabPanel, QHeader,QTable,
|
||||||
QSeparator, QCardActions, QDialog, QIcon
|
QSeparator, QCardActions, QDialog, QIcon, QSpace
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
:rows="users"
|
:rows="users"
|
||||||
:columns="userColumns"
|
:columns="userColumns"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
@row-click="openEdit('users', $event)"
|
@row-click="onRowClick"
|
||||||
:loading="loadingUsers"
|
:loading="loadingUsers"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
@ -40,12 +40,22 @@
|
|||||||
<!-- Команды -->
|
<!-- Команды -->
|
||||||
<q-tab-panel name="teams">
|
<q-tab-panel name="teams">
|
||||||
<div class="violet-card q-pa-md">
|
<div class="violet-card q-pa-md">
|
||||||
|
|
||||||
|
<div class="q-gutter-sm q-mb-sm row items-center justify-between">
|
||||||
|
<q-btn
|
||||||
|
label="Создание команды"
|
||||||
|
color="primary"
|
||||||
|
@click="createTeamHandler"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<q-table
|
<q-table
|
||||||
title="Команды"
|
title="Команды"
|
||||||
:rows="teams"
|
:rows="teams"
|
||||||
:columns="teamColumns"
|
:columns="teamColumns"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
@row-click="openEdit('teams', $event)"
|
@row-click="onRowClick"
|
||||||
:loading="loadingTeams"
|
:loading="loadingTeams"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
@ -53,6 +63,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
|
|
||||||
<!-- Проекты -->
|
<!-- Проекты -->
|
||||||
<q-tab-panel name="projects">
|
<q-tab-panel name="projects">
|
||||||
<div class="violet-card q-pa-md">
|
<div class="violet-card q-pa-md">
|
||||||
@ -61,7 +72,7 @@
|
|||||||
:rows="projects"
|
:rows="projects"
|
||||||
:columns="projectColumns"
|
:columns="projectColumns"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
@row-click="openEdit('projects', $event)"
|
@row-click="onRowClick"
|
||||||
:loading="loadingProjects"
|
:loading="loadingProjects"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
@ -77,7 +88,7 @@
|
|||||||
:rows="contests"
|
:rows="contests"
|
||||||
:columns="contestColumns"
|
:columns="contestColumns"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
@row-click="openEdit('contests', $event)"
|
@row-click="onRowClick"
|
||||||
:loading="loadingContests"
|
:loading="loadingContests"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
@ -92,54 +103,128 @@
|
|||||||
<q-dialog v-model="dialogVisible" persistent>
|
<q-dialog v-model="dialogVisible" persistent>
|
||||||
<q-card style="min-width: 350px; max-width: 700px;">
|
<q-card style="min-width: 350px; max-width: 700px;">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-h6">Редактирование {{ dialogType }}</div>
|
<div class="text-h6">
|
||||||
|
<template v-if="dialogType === 'teams'">
|
||||||
|
Редактирование команды {{ dialogData.title || '' }}
|
||||||
|
</template>
|
||||||
|
<template v-else-if="dialogType === 'users'">
|
||||||
|
Редактирование пользователя {{ dialogData.name || dialogData.login || '' }}
|
||||||
|
</template>
|
||||||
|
<template v-else-if="dialogType === 'projects'">
|
||||||
|
Редактирование проекта {{ dialogData.title || '' }}
|
||||||
|
</template>
|
||||||
|
<template v-else-if="dialogType === 'contests'">
|
||||||
|
Редактирование конкурса {{ dialogData.title || '' }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Редактирование {{ dialogType }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<q-separator />
|
<q-separator />
|
||||||
|
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<pre>{{ dialogData }}</pre>
|
<!-- Teams -->
|
||||||
|
<template v-if="dialogType === 'teams'">
|
||||||
|
<q-input v-model="dialogData.title" label="Название команды" dense autofocus clearable />
|
||||||
|
<q-input v-model="dialogData.description" label="Описание" dense clearable type="textarea" class="q-mt-sm" />
|
||||||
|
<q-input v-model="dialogData.logo" label="Логотип" dense clearable class="q-mt-sm" />
|
||||||
|
<q-input v-model="dialogData.git_url" label="Git URL" dense clearable class="q-mt-sm" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Users -->
|
||||||
|
<template v-else-if="dialogType === 'users'">
|
||||||
|
<q-input v-model="dialogData.login" label="Логин" dense autofocus clearable />
|
||||||
|
<q-input v-model="dialogData.name" label="Имя" dense clearable class="q-mt-sm" />
|
||||||
|
<q-input v-model="dialogData.email" label="Email" dense clearable class="q-mt-sm" />
|
||||||
|
<!-- Добавь другие поля, которые нужны для пользователя -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Projects -->
|
||||||
|
<template v-else-if="dialogType === 'projects'">
|
||||||
|
<q-input v-model="dialogData.title" label="Название проекта" dense autofocus clearable />
|
||||||
|
<q-input v-model="dialogData.description" label="Описание" dense clearable type="textarea" class="q-mt-sm" />
|
||||||
|
<q-input v-model="dialogData.repo_url" label="URL репозитория" dense clearable class="q-mt-sm" />
|
||||||
|
<!-- Другие поля проекта -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Contests -->
|
||||||
|
<template v-else-if="dialogType === 'contests'">
|
||||||
|
<q-input v-model="dialogData.title" label="Название конкурса" dense autofocus clearable />
|
||||||
|
<q-input v-model="dialogData.description" label="Описание" dense clearable type="textarea" class="q-mt-sm" />
|
||||||
|
<q-input v-model="dialogData.start_date" label="Дата начала" dense clearable class="q-mt-sm" type="date" />
|
||||||
|
<q-input v-model="dialogData.end_date" label="Дата окончания" dense clearable class="q-mt-sm" type="date" />
|
||||||
|
<!-- Другие поля конкурса -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<pre>{{ dialogData }}</pre>
|
||||||
|
</template>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
|
<q-btn
|
||||||
|
v-if="dialogType === 'teams'"
|
||||||
|
flat
|
||||||
|
label="Удалить"
|
||||||
|
color="negative"
|
||||||
|
@click="deleteTeam"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
v-if="dialogType === 'users'"
|
||||||
|
flat
|
||||||
|
label="Удалить"
|
||||||
|
color="negative"
|
||||||
|
@click="deleteUser"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
v-if="dialogType === 'projects'"
|
||||||
|
flat
|
||||||
|
label="Удалить"
|
||||||
|
color="negative"
|
||||||
|
@click="deleteProject"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
v-if="dialogType === 'contests'"
|
||||||
|
flat
|
||||||
|
label="Удалить"
|
||||||
|
color="negative"
|
||||||
|
@click="deleteContest"
|
||||||
|
/>
|
||||||
|
<q-space />
|
||||||
<q-btn flat label="Закрыть" color="primary" @click="closeDialog" />
|
<q-btn flat label="Закрыть" color="primary" @click="closeDialog" />
|
||||||
|
<q-btn
|
||||||
|
v-if="['teams', 'users', 'projects', 'contests'].includes(dialogType)"
|
||||||
|
flat
|
||||||
|
label="Сохранить"
|
||||||
|
color="primary"
|
||||||
|
@click="saveChanges"
|
||||||
|
/>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, onMounted } from 'vue'
|
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
// Импорты API
|
|
||||||
import fetchTeams from '@/api/teams/getTeams.js'
|
import fetchTeams from '@/api/teams/getTeams.js'
|
||||||
import fetchUsers from '@/api/users/getUsers.js'
|
|
||||||
import fetchProjects from '@/api/projects/getProjects.js'
|
|
||||||
import fetchContests from '@/api/contests/getContests.js'
|
|
||||||
import updateTeam from '@/api/teams/updateTeam.js'
|
import updateTeam from '@/api/teams/updateTeam.js'
|
||||||
|
import deleteTeamById from '@/api/teams/deleteTeam.js'
|
||||||
|
import createTeam from '@/api/teams/createTeam.js'
|
||||||
|
|
||||||
// Активная вкладка
|
|
||||||
const tab = ref('users')
|
|
||||||
|
|
||||||
// Данные
|
const tab = ref('teams')
|
||||||
const users = ref([])
|
|
||||||
const teams = ref([])
|
const teams = ref([])
|
||||||
const projects = ref([])
|
|
||||||
const contests = ref([])
|
|
||||||
|
|
||||||
// Загрузка
|
|
||||||
const loadingUsers = ref(false)
|
|
||||||
const loadingTeams = ref(false)
|
const loadingTeams = ref(false)
|
||||||
const loadingProjects = ref(false)
|
|
||||||
const loadingContests = ref(false)
|
|
||||||
|
|
||||||
// Колонки
|
const dialogVisible = ref(false)
|
||||||
const userColumns = [
|
const dialogData = ref({})
|
||||||
{ name: 'id', label: 'ID', field: 'id', sortable: true },
|
const dialogType = ref('')
|
||||||
{ name: 'name', label: 'Имя', field: 'name', sortable: true },
|
|
||||||
{ name: 'email', label: 'Email', field: 'email', sortable: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
const teamColumns = [
|
const teamColumns = [
|
||||||
{ name: 'title', label: 'Название команды', field: 'title', sortable: true },
|
{ name: 'title', label: 'Название команды', field: 'title', sortable: true },
|
||||||
@ -148,110 +233,107 @@ const teamColumns = [
|
|||||||
{ name: 'git_url', label: 'Git URL', field: 'git_url', sortable: true },
|
{ name: 'git_url', label: 'Git URL', field: 'git_url', sortable: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
const projectColumns = [
|
|
||||||
{ name: 'id', label: 'ID', field: 'id', sortable: true },
|
|
||||||
{ name: 'projectName', label: 'Проект', field: 'projectName', sortable: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
const contestColumns = [
|
|
||||||
{ name: 'id', label: 'ID', field: 'id', sortable: true },
|
|
||||||
{ name: 'contestName', label: 'Конкурс', field: 'contestName', sortable: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
// Модалка
|
|
||||||
const dialogVisible = ref(false)
|
|
||||||
const dialogData = ref({})
|
|
||||||
const dialogType = ref('')
|
|
||||||
|
|
||||||
// Функция открытия редактирования
|
|
||||||
function openEdit(type, row) {
|
function openEdit(type, row) {
|
||||||
|
console.log('openEdit called with row:', row)
|
||||||
dialogType.value = type
|
dialogType.value = type
|
||||||
// Клонируем объект, чтобы не менять данные в таблице до сохранения
|
// Проверка на null или undefined
|
||||||
dialogData.value = JSON.parse(JSON.stringify(row))
|
if (row) {
|
||||||
dialogVisible.value = true
|
dialogData.value = JSON.parse(JSON.stringify(row))
|
||||||
}
|
dialogVisible.value = true
|
||||||
|
|
||||||
// Функция сохранения изменений
|
|
||||||
async function saveChanges() {
|
|
||||||
if (dialogType.value === 'teams') {
|
|
||||||
try {
|
|
||||||
await updateTeam(dialogData.value)
|
|
||||||
// Обновляем локальные данные команды
|
|
||||||
const index = teams.value.findIndex(t => t.id === dialogData.value.id)
|
|
||||||
if (index !== -1) {
|
|
||||||
teams.value[index] = JSON.parse(JSON.stringify(dialogData.value))
|
|
||||||
}
|
|
||||||
closeDialog()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Ошибка при сохранении:', error.message)
|
|
||||||
// Можно добавить уведомление об ошибке
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Закрыть модалку
|
function onRowClick(event, row) {
|
||||||
|
console.log('event:', event)
|
||||||
|
console.log('row:', row)
|
||||||
|
openEdit('teams', row)
|
||||||
|
}
|
||||||
|
|
||||||
function closeDialog() {
|
function closeDialog() {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка данных
|
async function saveChanges() {
|
||||||
async function loadData(name) {
|
if (dialogType.value === 'teams') {
|
||||||
switch (name) {
|
try {
|
||||||
case 'users':
|
if (dialogData.value.id) {
|
||||||
loadingUsers.value = true
|
// Формируем чистый объект с нужными полями
|
||||||
try {
|
const teamToUpdate = {
|
||||||
users.value = await fetchUsers() || []
|
id: dialogData.value.id,
|
||||||
} catch (error) {
|
title: dialogData.value.title,
|
||||||
users.value = []
|
description: dialogData.value.description,
|
||||||
console.error(error.message)
|
logo: dialogData.value.logo,
|
||||||
} finally {
|
git_url: dialogData.value.git_url
|
||||||
loadingUsers.value = false
|
}
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'teams':
|
console.log('Отправляем на сервер:', teamToUpdate)
|
||||||
loadingTeams.value = true
|
await updateTeam(teamToUpdate)
|
||||||
try {
|
|
||||||
teams.value = await fetchTeams() || []
|
|
||||||
} catch (error) {
|
|
||||||
teams.value = []
|
|
||||||
console.error(error.message)
|
|
||||||
} finally {
|
|
||||||
loadingTeams.value = false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'projects':
|
const index = teams.value.findIndex(t => t.id === teamToUpdate.id)
|
||||||
loadingProjects.value = true
|
if (index !== -1) {
|
||||||
try {
|
teams.value[index] = JSON.parse(JSON.stringify(teamToUpdate))
|
||||||
projects.value = await fetchProjects() || []
|
}
|
||||||
} catch (error) {
|
} else {
|
||||||
projects.value = []
|
const newTeam = await createTeam(dialogData.value)
|
||||||
console.error(error.message)
|
teams.value.push(newTeam)
|
||||||
} finally {
|
|
||||||
loadingProjects.value = false
|
|
||||||
}
|
}
|
||||||
break
|
closeDialog()
|
||||||
|
} catch (error) {
|
||||||
case 'contests':
|
console.error('Ошибка при сохранении:', error.message)
|
||||||
loadingContests.value = true
|
}
|
||||||
try {
|
|
||||||
contests.value = await fetchContests() || []
|
|
||||||
} catch (error) {
|
|
||||||
contests.value = []
|
|
||||||
console.error(error.message)
|
|
||||||
} finally {
|
|
||||||
loadingContests.value = false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Начальная загрузка
|
|
||||||
onMounted(() => loadData(tab.value))
|
|
||||||
|
|
||||||
// Загрузка при смене вкладки
|
async function loadData(name) {
|
||||||
|
if (name === 'teams') {
|
||||||
|
loadingTeams.value = true
|
||||||
|
try {
|
||||||
|
teams.value = await fetchTeams() || []
|
||||||
|
} catch (error) {
|
||||||
|
teams.value = []
|
||||||
|
console.error(error.message)
|
||||||
|
} finally {
|
||||||
|
loadingTeams.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('teams loaded:', teams.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTeam() {
|
||||||
|
if (!dialogData.value.id) return
|
||||||
|
try {
|
||||||
|
await deleteTeamById(dialogData.value.id)
|
||||||
|
teams.value = teams.value.filter(t => t.id !== dialogData.value.id)
|
||||||
|
closeDialog()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при удалении команды:', error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTeamHandler() {
|
||||||
|
dialogType.value = 'teams'
|
||||||
|
dialogData.value = {
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
logo: '',
|
||||||
|
git_url: ''
|
||||||
|
}
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData(tab.value)
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
loadData(tab.value)
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
watch(tab, (newTab) => {
|
watch(tab, (newTab) => {
|
||||||
loadData(newTab)
|
loadData(newTab)
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user