/
Codename-Nik
/
WebApp
Обзор
Документация
Войти
/
Codename-Nik
/
WebApp
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
frontend/src/views/TaskDetailView.vue
308 строк
10 KB
Nikolay Pokhodnya
first commit
03 ноя 2025, 19:47
03 ноя 2025, 19:47
a9fbdee
Код
Авторство
О чём код?
<template> <div> <v-row> <v-col cols="12"> <div class="d-flex align-center mb-4"> <v-btn icon @click="$router.back()" class="mr-2" > <v-icon>mdi-arrow-left</v-icon> </v-btn> <h1 class="text-h4 font-weight-bold">Детали задачи</h1> </div> </v-col> </v-row> <v-row v-if="loading"> <v-col cols="12"> <v-skeleton-loader type="card"></v-skeleton-loader> </v-col> </v-row> <v-row v-else-if="task"> <v-col cols="12" md="8"> <v-card class="mb-4"> <v-card-title class="d-flex justify-space-between align-center"> <span>{{ task.title }}</span> <v-chip :color="getStatusColor(task.status)" size="small"> {{ getStatusText(task.status) }} </v-chip> </v-card-title> <v-card-text> <div class="mb-4"> <h3 class="text-h6 mb-2">Описание</h3> <p class="text-body-1">{{ task.description || 'Описание отсутствует' }}</p> </div> <v-row> <v-col cols="6"> <div class="text-caption text-grey">Приоритет</div> <v-chip :color="getPriorityColor(task.priority)" size="small"> {{ getPriorityText(task.priority) }} </v-chip> </v-col> <v-col cols="6"> <div class="text-caption text-grey">Срок выполнения</div> <div>{{ task.due_date ? formatDate(task.due_date) : 'Не установлен' }}</div> </v-col> </v-row> <v-row class="mt-2"> <v-col cols="6"> <div class="text-caption text-grey">Проект</div> <div>{{ task.project_name || 'Без проекта' }}</div> </v-col> <v-col cols="6"> <div class="text-caption text-grey">Назначена</div> <div>{{ task.assigned_to_name || 'Не назначена' }}</div> </v-col> </v-row> </v-card-text> <v-card-actions> <v-btn color="primary" @click="editTask"> <v-icon class="mr-2">mdi-pencil</v-icon> Редактировать </v-btn> <v-btn color="error" variant="outlined" @click="deleteTask"> <v-icon class="mr-2">mdi-delete</v-icon> Удалить </v-btn> </v-card-actions> </v-card> <v-card> <v-card-title> <v-icon class="mr-2">mdi-comment</v-icon> Комментарии ({{ task.comment_count || 0 }}) </v-card-title> <v-card-text> <div v-if="!task.comments || task.comments.length === 0" class="text-center py-4"> <v-icon size="64" color="grey-lighten-2">mdi-comment-outline</v-icon> <div class="text-h6 mt-2">Комментарии отсутствуют</div> </div> <div v-else> <v-list> <v-list-item v-for="comment in task.comments" :key="comment.id" > <template v-slot:prepend> <v-avatar size="40" color="primary" class="mr-3"> <span class="text-white">{{ getInitials(comment.author_name) }}</span> </v-avatar> </template> <v-list-item-title>{{ comment.author_name }}</v-list-item-title> <v-list-item-subtitle>{{ comment.content }}</v-list-item-subtitle> <template v-slot:append> <div class="text-caption text-grey"> {{ formatDate(comment.created_at) }} </div> </template> </v-list-item> </v-list> </div> <v-form @submit.prevent="addComment" class="mt-4"> <v-textarea v-model="newComment" label="Добавить комментарий" variant="outlined" rows="3" :loading="commentLoading" ></v-textarea> <v-btn type="submit" color="primary" :disabled="!newComment.trim()" :loading="commentLoading" > Добавить комментарий </v-btn> </v-form> </v-card-text> </v-card> </v-col> <v-col cols="12" md="4"> <v-card> <v-card-title>Информация</v-card-title> <v-card-text> <v-list> <v-list-item> <template v-slot:prepend> <v-icon>mdi-account</v-icon> </template> <v-list-item-title>Создатель</v-list-item-title> <v-list-item-subtitle>{{ task.created_by_name }}</v-list-item-subtitle> </v-list-item> <v-list-item> <template v-slot:prepend> <v-icon>mdi-calendar</v-icon> </template> <v-list-item-title>Создана</v-list-item-title> <v-list-item-subtitle>{{ formatDate(task.created_at) }}</v-list-item-subtitle> </v-list-item> <v-list-item> <template v-slot:prepend> <v-icon>mdi-update</v-icon> </template> <v-list-item-title>Обновлена</v-list-item-title> <v-list-item-subtitle>{{ formatDate(task.updated_at) }}</v-list-item-subtitle> </v-list-item> </v-list> </v-card-text> </v-card> </v-col> </v-row> <v-row v-else> <v-col cols="12"> <v-card> <v-card-text class="text-center py-8"> <v-icon size="64" color="error">mdi-alert-circle</v-icon> <div class="text-h6 mt-2">Задача не найдена</div> <v-btn @click="$router.push('/tasks')" color="primary" class="mt-4"> Вернуться к списку задач </v-btn> </v-card-text> </v-card> </v-col> </v-row> </div> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useTasksStore } from '@/stores/tasks' import { useNotificationsStore } from '@/stores/notifications' const route = useRoute() const router = useRouter() const tasksStore = useTasksStore() const notifications = useNotificationsStore() const loading = ref(true) const task = ref<any>(null) const newComment = ref('') const commentLoading = ref(false) onMounted(async () => { const taskId = parseInt(route.params.id as string) if (isNaN(taskId)) { notifications.error('Неверный ID задачи') router.push('/tasks') return } try { task.value = await tasksStore.fetchTask(taskId) } catch (error) { notifications.error('Ошибка при загрузке задачи') router.push('/tasks') } finally { loading.value = false } }) const getStatusColor = (status: string) => { const colors: { [key: string]: string } = { todo: 'grey', in_progress: 'warning', review: 'info', done: 'success' } return colors[status] || 'grey' } const getStatusText = (status: string) => { const texts: { [key: string]: string } = { todo: 'To Do', in_progress: 'In Progress', review: 'Review', done: 'Done' } return texts[status] || status } const getPriorityColor = (priority: string) => { const colors: { [key: string]: string } = { low: 'success', medium: 'warning', high: 'error', urgent: 'deep-purple' } return colors[priority] || 'grey' } const getPriorityText = (priority: string) => { const texts: { [key: string]: string } = { low: 'Low', medium: 'Medium', high: 'High', urgent: 'Urgent' } return texts[priority] || priority } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' }) } const getInitials = (name: string) => { if (!name) return '?' return name.split(' ').map(n => n[0]).join('').toUpperCase() } const editTask = () => { notifications.info('Редактирование задачи') } const deleteTask = async () => { if (!task.value) return if (confirm('Вы уверены, что хотите удалить эту задачу?')) { try { await tasksStore.deleteTask(task.value.id) notifications.success('Задача удалена') router.push('/tasks') } catch (error) { notifications.error('Ошибка при удалении задачи') } } } const addComment = async () => { if (!newComment.value.trim() || !task.value) return commentLoading.value = true try { await new Promise(resolve => setTimeout(resolve, 1000)) notifications.success('Комментарий добавлен') newComment.value = '' task.value = await tasksStore.fetchTask(task.value.id) } catch (error) { notifications.error('Ошибка при добавлении комментария') } finally { commentLoading.value = false } } </script>