сделал проекты

This commit is contained in:
Мельников Данил 2025-05-31 12:23:44 +05:00
parent ac42a8da7f
commit 5b1e74876d
11 changed files with 332 additions and 52 deletions

View File

@ -0,0 +1,23 @@
import axios from 'axios'
import CONFIG from '@/core/config.js'
const createProject = async (project) => {
try {
const token = localStorage.getItem('access_token') // или другой способ получения токена
const response = await axios.post(
`${CONFIG.BASE_URL}/projects`,
project,
{
withCredentials: true,
headers: {
Authorization: `Bearer ${token}`
}
}
)
return response.data
} catch (error) {
throw new Error(error.response?.data?.detail || error.message)
}
}
export default createProject

View File

@ -0,0 +1,22 @@
import axios from 'axios'
import CONFIG from '@/core/config.js'
const deleteProject = async (projectId) => {
try {
const token = localStorage.getItem('access_token') // получение токена
const response = await axios.delete(
`${CONFIG.BASE_URL}/projects/${projectId}`,
{
withCredentials: true,
headers: {
Authorization: `Bearer ${token}`
}
}
)
return response.data
} catch (error) {
throw new Error(error.response?.data?.detail || error.message)
}
}
export default deleteProject

View File

@ -3,13 +3,19 @@ import CONFIG from "@/core/config.js";
const fetchProjects = async () => { const fetchProjects = async () => {
try { try {
const token = localStorage.getItem("access_token");
const response = await axios.get(`${CONFIG.BASE_URL}/projects`, { const response = await axios.get(`${CONFIG.BASE_URL}/projects`, {
withCredentials: true, headers: {
Authorization: `Bearer ${token}`,
},
}); });
return response.data; return response.data;
} catch (error) { } catch (error) {
if (error.response?.status === 401) { if (error.response?.status === 401) {
throw new Error("Нет доступа к проектам (401)"); throw new Error("Нет доступа к проектам (401)");
} else if (error.response?.status === 403) {
throw new Error("Доступ запрещён (403)");
} }
throw new Error(error.message); throw new Error(error.message);
} }

View File

@ -0,0 +1,31 @@
import axios from 'axios'
import CONFIG from '@/core/config.js'
const updateProject = async (project) => {
try {
const token = localStorage.getItem('access_token')
// Убираем id из тела запроса, он идет в URL
const { id, ...projectData } = project
console.log('Отправляем на сервер:', projectData)
const response = await axios.put(
`${CONFIG.BASE_URL}/projects/${id}`,
projectData,
{
withCredentials: true,
headers: {
Authorization: `Bearer ${token}`
}
}
)
console.log('Ответ от сервера:', response.data)
return response.data
} catch (error) {
throw new Error(error.response?.data?.detail || error.message)
}
}
export default updateProject

View File

@ -3,9 +3,15 @@ import CONFIG from '@/core/config.js'
const deleteTeam = async (teamId) => { const deleteTeam = async (teamId) => {
try { try {
const token = localStorage.getItem('access_token') // получение токена
const response = await axios.delete( const response = await axios.delete(
`${CONFIG.BASE_URL}/teams/${teamId}`, `${CONFIG.BASE_URL}/teams/${teamId}`,
{ withCredentials: true } {
withCredentials: true,
headers: {
Authorization: `Bearer ${token}`
}
}
) )
return response.data return response.data
} catch (error) { } catch (error) {

View File

@ -0,0 +1,26 @@
<template>
<q-dialog v-model="visible" persistent>
<q-card style="min-width: 350px; max-width: 700px;">
<q-card-section>
<div class="text-h6">{{ title }}</div>
</q-card-section>
<q-separator />
<q-card-section>
<slot />
</q-card-section>
<q-card-actions align="right">
<slot name="actions" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup>
defineProps({
visible: Boolean,
title: String
})
</script>

View File

@ -0,0 +1,37 @@
<template>
<SharedDialogWrapper :visible="visible" title="Редактирование конкурса">
<q-input v-model="data.title" label="Название конкурса" dense autofocus clearable />
<q-input v-model="data.description" label="Описание" dense clearable type="textarea" class="q-mt-sm" />
<q-input v-model="data.start_date" label="Дата начала" type="date" dense clearable class="q-mt-sm" />
<q-input v-model="data.end_date" label="Дата окончания" type="date" dense clearable class="q-mt-sm" />
<template #actions>
<q-btn flat label="Удалить" color="negative" @click="onDelete" v-if="showDelete" />
<q-space />
<q-btn flat label="Закрыть" color="primary" @click="onClose" />
<q-btn flat label="Сохранить" color="primary" @click="onSave" />
</template>
</SharedDialogWrapper>
</template>
<script setup>
import SharedDialogWrapper from './SharedDialogWrapper.vue'
defineProps({
visible: Boolean,
data: Object,
showDelete: Boolean
})
const emit = defineEmits(['save', 'close', 'delete'])
function onSave() {
emit('save')
}
function onClose() {
emit('close')
}
function onDelete() {
emit('delete')
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<SharedDialogWrapper :visible="visible" title="Редактирование проекта">
<q-input v-model="data.title" label="Название проекта" dense autofocus clearable />
<q-input v-model="data.description" label="Описание" dense clearable type="textarea" class="q-mt-sm" />
<q-input v-model="data.repo_url" label="URL репозитория" dense clearable class="q-mt-sm" />
<template #actions>
<q-btn flat label="Удалить" color="negative" @click="onDelete" v-if="showDelete" />
<q-space />
<q-btn flat label="Закрыть" color="primary" @click="onClose" />
<q-btn flat label="Сохранить" color="primary" @click="onSave" />
</template>
</SharedDialogWrapper>
</template>
<script setup>
import SharedDialogWrapper from './SharedDialogWrapper.vue'
defineProps({
visible: Boolean,
data: Object,
showDelete: Boolean
})
const emit = defineEmits(['save', 'close', 'delete'])
function onSave() {
emit('save')
}
function onClose() {
emit('close')
}
function onDelete() {
emit('delete')
}
</script>

View File

@ -0,0 +1,37 @@
<template>
<SharedDialogWrapper :visible="visible" title="Редактирование команды">
<q-input v-model="data.title" label="Название команды" dense autofocus clearable />
<q-input v-model="data.description" label="Описание" dense clearable type="textarea" class="q-mt-sm" />
<q-input v-model="data.logo" label="Логотип" dense clearable class="q-mt-sm" />
<q-input v-model="data.git_url" label="Git URL" dense clearable class="q-mt-sm" />
<template #actions>
<q-btn flat label="Удалить" color="negative" @click="onDelete" v-if="showDelete" />
<q-space />
<q-btn flat label="Закрыть" color="primary" @click="onClose" />
<q-btn flat label="Сохранить" color="primary" @click="onSave" />
</template>
</SharedDialogWrapper>
</template>
<script setup>
import SharedDialogWrapper from './SharedDialogWrapper.vue'
defineProps({
visible: Boolean,
data: Object,
showDelete: Boolean
})
const emit = defineEmits(['save', 'close', 'delete'])
function onSave() {
emit('save')
}
function onClose() {
emit('close')
}
function onDelete() {
emit('delete')
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<SharedDialogWrapper :visible="visible" title="Редактирование пользователя">
<q-input v-model="data.login" label="Логин" dense autofocus clearable />
<q-input v-model="data.name" label="Имя" dense clearable class="q-mt-sm" />
<q-input v-model="data.email" label="Email" dense clearable class="q-mt-sm" />
<template #actions>
<q-btn flat label="Удалить" color="negative" @click="onDelete" v-if="showDelete" />
<q-space />
<q-btn flat label="Закрыть" color="primary" @click="onClose" />
<q-btn flat label="Сохранить" color="primary" @click="onSave" />
</template>
</SharedDialogWrapper>
</template>
<script setup>
import SharedDialogWrapper from '@/components/dialogs/SharedDialogWrapper.vue'
defineProps({
visible: Boolean,
data: Object,
showDelete: Boolean
})
const emit = defineEmits(['save', 'close', 'delete'])
function onSave() {
emit('save')
}
function onClose() {
emit('close')
}
function onDelete() {
emit('delete')
}
</script>

View File

@ -45,7 +45,7 @@
<q-btn <q-btn
label="Создание команды" label="Создание команды"
color="primary" color="primary"
@click="createTeamHandler" @click="createHandler"
/> />
</div> </div>
@ -169,7 +169,7 @@
flat flat
label="Удалить" label="Удалить"
color="negative" color="negative"
@click="deleteTeam" @click="deleteItem"
/> />
<q-btn <q-btn
v-if="dialogType === 'users'" v-if="dialogType === 'users'"
@ -216,16 +216,17 @@ import updateTeam from '@/api/teams/updateTeam.js'
import deleteTeamById from '@/api/teams/deleteTeam.js' import deleteTeamById from '@/api/teams/deleteTeam.js'
import createTeam from '@/api/teams/createTeam.js' import createTeam from '@/api/teams/createTeam.js'
import fetchProjects from '@/api/projects/getProjects.js'
import updateProject from '@/api/projects/updateProject.js'
import deleteProjectById from '@/api/projects/deleteProject.js'
import createProject from '@/api/projects/createProject.js'
// Текущая вкладка 'teams' или 'projects'
const tab = ref('teams') const tab = ref('teams')
// --- Teams ---
const teams = ref([]) const teams = ref([])
const loadingTeams = ref(false) const loadingTeams = ref(false)
const dialogVisible = ref(false)
const dialogData = ref({})
const dialogType = ref('')
const teamColumns = [ const teamColumns = [
{ name: 'title', label: 'Название команды', field: 'title', sortable: true }, { name: 'title', label: 'Название команды', field: 'title', sortable: true },
{ name: 'description', label: 'Описание', field: 'description', sortable: true }, { name: 'description', label: 'Описание', field: 'description', sortable: true },
@ -233,20 +234,37 @@ const teamColumns = [
{ name: 'git_url', label: 'Git URL', field: 'git_url', sortable: true }, { name: 'git_url', label: 'Git URL', field: 'git_url', sortable: true },
] ]
// --- Projects ---
const projects = ref([])
const loadingProjects = ref(false)
const projectColumns = [
{ name: 'name', label: 'Название проекта', field: 'name', sortable: true },
{ name: 'summary', label: 'Описание', field: 'summary', sortable: true },
{ name: 'deadline', label: 'Дедлайн', field: 'deadline', sortable: true },
// добавьте нужные поля
]
// Общие состояния для диалогов
const dialogVisible = ref(false)
const dialogData = ref({})
const dialogType = ref('') // 'teams' или 'projects'
function openEdit(type, row) { function openEdit(type, row) {
console.log('openEdit called with row:', row)
dialogType.value = type dialogType.value = type
// Проверка на null или undefined
if (row) { if (row) {
dialogData.value = JSON.parse(JSON.stringify(row)) dialogData.value = JSON.parse(JSON.stringify(row))
dialogVisible.value = true } else {
if (type === 'teams') {
dialogData.value = { title: '', description: '', logo: '', git_url: '' }
} else if (type === 'projects') {
dialogData.value = { name: '', summary: '', deadline: '' }
} }
}
dialogVisible.value = true
} }
function onRowClick(event, row) { function onRowClick(event, row) {
console.log('event:', event) openEdit(tab.value, row)
console.log('row:', row)
openEdit('teams', row)
} }
function closeDialog() { function closeDialog() {
@ -254,37 +272,32 @@ function closeDialog() {
} }
async function saveChanges() { async function saveChanges() {
if (dialogType.value === 'teams') {
try { try {
if (dialogType.value === 'teams') {
if (dialogData.value.id) { if (dialogData.value.id) {
// Формируем чистый объект с нужными полями await updateTeam(dialogData.value)
const teamToUpdate = { const idx = teams.value.findIndex(t => t.id === dialogData.value.id)
id: dialogData.value.id, if (idx !== -1) teams.value[idx] = JSON.parse(JSON.stringify(dialogData.value))
title: dialogData.value.title,
description: dialogData.value.description,
logo: dialogData.value.logo,
git_url: dialogData.value.git_url
}
console.log('Отправляем на сервер:', teamToUpdate)
await updateTeam(teamToUpdate)
const index = teams.value.findIndex(t => t.id === teamToUpdate.id)
if (index !== -1) {
teams.value[index] = JSON.parse(JSON.stringify(teamToUpdate))
}
} else { } else {
const newTeam = await createTeam(dialogData.value) const newTeam = await createTeam(dialogData.value)
teams.value.push(newTeam) teams.value.push(newTeam)
} }
} else if (dialogType.value === 'projects') {
if (dialogData.value.id) {
await updateProject(dialogData.value)
const idx = projects.value.findIndex(p => p.id === dialogData.value.id)
if (idx !== -1) projects.value[idx] = JSON.parse(JSON.stringify(dialogData.value))
} else {
const newProject = await createProject(dialogData.value)
projects.value.push(newProject)
}
}
closeDialog() closeDialog()
} catch (error) { } catch (error) {
console.error('Ошибка при сохранении:', error.message) console.error('Ошибка при сохранении:', error.message)
} }
}
} }
async function loadData(name) { async function loadData(name) {
if (name === 'teams') { if (name === 'teams') {
loadingTeams.value = true loadingTeams.value = true
@ -296,30 +309,37 @@ async function loadData(name) {
} finally { } finally {
loadingTeams.value = false loadingTeams.value = false
} }
} else if (name === 'projects') {
loadingProjects.value = true
try {
projects.value = await fetchProjects() || []
} catch (error) {
projects.value = []
console.error(error.message)
} finally {
loadingProjects.value = false
}
} }
console.log('teams loaded:', teams.value)
} }
async function deleteTeam() { async function deleteItem() {
if (!dialogData.value.id) return if (!dialogData.value.id) return
try { try {
if (dialogType.value === 'teams') {
await deleteTeamById(dialogData.value.id) await deleteTeamById(dialogData.value.id)
teams.value = teams.value.filter(t => t.id !== dialogData.value.id) teams.value = teams.value.filter(t => t.id !== dialogData.value.id)
} else if (dialogType.value === 'projects') {
await deleteProjectById(dialogData.value.id)
projects.value = projects.value.filter(p => p.id !== dialogData.value.id)
}
closeDialog() closeDialog()
} catch (error) { } catch (error) {
console.error('Ошибка при удалении команды:', error.message) console.error('Ошибка при удалении:', error.message)
} }
} }
function createTeamHandler() { function createHandler() {
dialogType.value = 'teams' openEdit(tab.value, null)
dialogData.value = {
title: '',
description: '',
logo: '',
git_url: ''
}
dialogVisible.value = true
} }
onMounted(() => { onMounted(() => {