Добавление проектов
Добавили возможность создавания разных проектов.
This commit is contained in:
@@ -9,6 +9,12 @@
|
||||
<Header title="Архив задач">
|
||||
<template #filters>
|
||||
<div class="filters">
|
||||
<!-- Выбор проекта -->
|
||||
<ProjectSelector @change="onProjectChange" />
|
||||
|
||||
<div class="filter-divider"></div>
|
||||
|
||||
<!-- Фильтр по отделам -->
|
||||
<button
|
||||
class="filter-tag"
|
||||
:class="{ active: activeDepartment === null }"
|
||||
@@ -17,7 +23,7 @@
|
||||
Все
|
||||
</button>
|
||||
<button
|
||||
v-for="dept in departments"
|
||||
v-for="dept in store.departments"
|
||||
:key="dept.id"
|
||||
class="filter-tag"
|
||||
:class="{ active: activeDepartment === dept.id }"
|
||||
@@ -44,8 +50,8 @@
|
||||
v-for="card in filteredCards"
|
||||
:key="card.id"
|
||||
:card="card"
|
||||
:departments="departments"
|
||||
:labels="labels"
|
||||
:departments="store.departments"
|
||||
:labels="store.labels"
|
||||
@click="openTaskPanel(card)"
|
||||
@restore="handleRestore"
|
||||
@delete="confirmDelete"
|
||||
@@ -73,9 +79,9 @@
|
||||
:card="editingCard"
|
||||
:column-id="null"
|
||||
:is-archived="true"
|
||||
:departments="departments"
|
||||
:labels="labels"
|
||||
:users="users"
|
||||
:departments="store.departments"
|
||||
:labels="store.labels"
|
||||
:users="store.users"
|
||||
@close="closePanel"
|
||||
@save="handleSaveTask"
|
||||
@delete="handleDeleteTask"
|
||||
@@ -101,26 +107,15 @@ import Header from '../components/Header.vue'
|
||||
import ArchiveCard from '../components/ArchiveCard.vue'
|
||||
import TaskPanel from '../components/TaskPanel.vue'
|
||||
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
||||
import { departmentsApi, labelsApi, cardsApi, usersApi } from '../api'
|
||||
import ProjectSelector from '../components/ProjectSelector.vue'
|
||||
import { useProjectsStore } from '../stores/projects'
|
||||
import { cardsApi } from '../api'
|
||||
|
||||
// Активный фильтр по отделу (синхронизация с основной доской)
|
||||
const savedDepartment = localStorage.getItem('activeDepartment')
|
||||
const activeDepartment = ref(savedDepartment ? parseInt(savedDepartment) : null)
|
||||
// ==================== STORE ====================
|
||||
const store = useProjectsStore()
|
||||
|
||||
// Сохраняем в localStorage при изменении
|
||||
watch(activeDepartment, (newVal) => {
|
||||
if (newVal === null) {
|
||||
localStorage.removeItem('activeDepartment')
|
||||
} else {
|
||||
localStorage.setItem('activeDepartment', newVal.toString())
|
||||
}
|
||||
})
|
||||
|
||||
// Данные
|
||||
const departments = ref([])
|
||||
const labels = ref([])
|
||||
// ==================== КАРТОЧКИ ====================
|
||||
const cards = ref([])
|
||||
const users = ref([])
|
||||
const loading = ref(true)
|
||||
|
||||
// Отфильтрованные карточки (сортируем по дате завершения, новые сверху)
|
||||
@@ -130,30 +125,24 @@ const filteredCards = computed(() => {
|
||||
result = result.filter(card => card.departmentId === activeDepartment.value)
|
||||
}
|
||||
return result.sort((a, b) => {
|
||||
// Сортировка по дате завершения (новые сверху)
|
||||
const dateA = a.dateClosed ? new Date(a.dateClosed).getTime() : 0
|
||||
const dateB = b.dateClosed ? new Date(b.dateClosed).getTime() : 0
|
||||
return dateB - dateA
|
||||
})
|
||||
})
|
||||
|
||||
// Загрузка данных
|
||||
const fetchData = async () => {
|
||||
// Загрузка архивных карточек
|
||||
const fetchCards = async () => {
|
||||
if (!store.currentProjectId) {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const [departmentsData, labelsData, cardsData, usersData] = await Promise.all([
|
||||
departmentsApi.getAll(),
|
||||
labelsApi.getAll(),
|
||||
cardsApi.getAll(1), // archive = 1
|
||||
usersApi.getAll()
|
||||
])
|
||||
|
||||
if (departmentsData.success) departments.value = departmentsData.data
|
||||
if (labelsData.success) labels.value = labelsData.data
|
||||
if (usersData.success) users.value = usersData.data
|
||||
|
||||
if (cardsData.success) {
|
||||
cards.value = cardsData.data.map(card => ({
|
||||
const result = await cardsApi.getAll(store.currentProjectId, 1) // archive = 1
|
||||
if (result.success) {
|
||||
cards.value = result.data.map(card => ({
|
||||
id: card.id,
|
||||
title: card.title,
|
||||
description: card.descript,
|
||||
@@ -175,14 +164,30 @@ const fetchData = async () => {
|
||||
}))
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки данных:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Панель редактирования
|
||||
// При смене проекта
|
||||
const onProjectChange = async () => {
|
||||
activeDepartment.value = null
|
||||
await fetchCards()
|
||||
}
|
||||
|
||||
// ==================== ФИЛЬТР ПО ОТДЕЛАМ ====================
|
||||
const savedDepartment = localStorage.getItem('activeDepartment')
|
||||
const activeDepartment = ref(savedDepartment ? parseInt(savedDepartment) : null)
|
||||
|
||||
watch(activeDepartment, (newVal) => {
|
||||
if (newVal === null) {
|
||||
localStorage.removeItem('activeDepartment')
|
||||
} else {
|
||||
localStorage.setItem('activeDepartment', newVal.toString())
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== ПАНЕЛЬ РЕДАКТИРОВАНИЯ ====================
|
||||
const panelOpen = ref(false)
|
||||
const editingCard = ref(null)
|
||||
|
||||
@@ -196,10 +201,8 @@ const closePanel = () => {
|
||||
editingCard.value = null
|
||||
}
|
||||
|
||||
// Сохранение задачи
|
||||
const handleSaveTask = async (taskData) => {
|
||||
if (taskData.id) {
|
||||
// Обновляем на сервере
|
||||
await cardsApi.update({
|
||||
id: taskData.id,
|
||||
id_department: taskData.departmentId,
|
||||
@@ -211,7 +214,6 @@ const handleSaveTask = async (taskData) => {
|
||||
descript_full: taskData.details
|
||||
})
|
||||
|
||||
// Обновляем локально
|
||||
const card = cards.value.find(c => c.id === taskData.id)
|
||||
if (card) {
|
||||
card.title = taskData.title
|
||||
@@ -228,7 +230,6 @@ const handleSaveTask = async (taskData) => {
|
||||
closePanel()
|
||||
}
|
||||
|
||||
// Удаление через панель
|
||||
const handleDeleteTask = async (cardId) => {
|
||||
const result = await cardsApi.delete(cardId)
|
||||
if (result.success) {
|
||||
@@ -237,7 +238,7 @@ const handleDeleteTask = async (cardId) => {
|
||||
closePanel()
|
||||
}
|
||||
|
||||
// Диалог подтверждения удаления
|
||||
// ==================== УДАЛЕНИЕ С ПОДТВЕРЖДЕНИЕМ ====================
|
||||
const confirmDialogOpen = ref(false)
|
||||
const cardToDelete = ref(null)
|
||||
|
||||
@@ -257,7 +258,7 @@ const handleConfirmDelete = async () => {
|
||||
cardToDelete.value = null
|
||||
}
|
||||
|
||||
// Восстановление из архива
|
||||
// ==================== ВОССТАНОВЛЕНИЕ ====================
|
||||
const handleRestore = async (cardId) => {
|
||||
const result = await cardsApi.setArchive(cardId, 0)
|
||||
if (result.success) {
|
||||
@@ -265,18 +266,17 @@ const handleRestore = async (cardId) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Восстановление через панель редактирования
|
||||
const handleRestoreFromPanel = async (cardId) => {
|
||||
await handleRestore(cardId)
|
||||
closePanel()
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
if (window.lucide) {
|
||||
window.lucide.createIcons()
|
||||
}
|
||||
// ==================== ИНИЦИАЛИЗАЦИЯ ====================
|
||||
onMounted(async () => {
|
||||
await store.init()
|
||||
await fetchCards()
|
||||
|
||||
if (window.lucide) window.lucide.createIcons()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -304,6 +304,14 @@ onMounted(() => {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Разделитель между проектом и отделами */
|
||||
.filter-divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
/* Кнопка фильтра */
|
||||
.filter-tag {
|
||||
padding: 6px 12px;
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
<Header title="Доска задач">
|
||||
<template #filters>
|
||||
<div class="filters">
|
||||
<!-- Выбор проекта -->
|
||||
<ProjectSelector @change="onProjectChange" />
|
||||
|
||||
<div class="filter-divider"></div>
|
||||
|
||||
<!-- Фильтр по отделам -->
|
||||
<button
|
||||
class="filter-tag"
|
||||
:class="{ active: activeDepartment === null }"
|
||||
@@ -17,7 +23,7 @@
|
||||
Все
|
||||
</button>
|
||||
<button
|
||||
v-for="dept in departments"
|
||||
v-for="dept in store.departments"
|
||||
:key="dept.id"
|
||||
class="filter-tag"
|
||||
:class="{ active: activeDepartment === dept.id }"
|
||||
@@ -52,10 +58,11 @@
|
||||
<Board
|
||||
ref="boardRef"
|
||||
:active-department="activeDepartment"
|
||||
:departments="departments"
|
||||
:labels="labels"
|
||||
:columns="columns"
|
||||
:departments="store.departments"
|
||||
:labels="store.labels"
|
||||
:columns="store.columns"
|
||||
:cards="cards"
|
||||
:done-column-id="store.doneColumnId"
|
||||
@stats-updated="stats = $event"
|
||||
@open-task="openTaskPanel"
|
||||
@create-task="openNewTaskPanel"
|
||||
@@ -68,9 +75,10 @@
|
||||
:show="panelOpen"
|
||||
:card="editingCard"
|
||||
:column-id="editingColumnId"
|
||||
:departments="departments"
|
||||
:labels="labels"
|
||||
:users="users"
|
||||
:done-column-id="store.doneColumnId"
|
||||
:departments="store.departments"
|
||||
:labels="store.labels"
|
||||
:users="store.users"
|
||||
@close="closePanel"
|
||||
@save="handleSaveTask"
|
||||
@delete="handleDeleteTask"
|
||||
@@ -85,14 +93,34 @@ import Sidebar from '../components/Sidebar.vue'
|
||||
import Header from '../components/Header.vue'
|
||||
import Board from '../components/Board.vue'
|
||||
import TaskPanel from '../components/TaskPanel.vue'
|
||||
import { departmentsApi, labelsApi, columnsApi, cardsApi, usersApi } from '../api'
|
||||
import ProjectSelector from '../components/ProjectSelector.vue'
|
||||
import { useProjectsStore } from '../stores/projects'
|
||||
import { cardsApi } from '../api'
|
||||
|
||||
// Активный фильтр по отделу (null = все)
|
||||
// Восстанавливаем из localStorage
|
||||
// ==================== STORE ====================
|
||||
const store = useProjectsStore()
|
||||
|
||||
// ==================== КАРТОЧКИ ====================
|
||||
const cards = ref([])
|
||||
|
||||
// Загрузка карточек текущего проекта
|
||||
const fetchCards = async () => {
|
||||
if (!store.currentProjectId) return
|
||||
|
||||
const result = await cardsApi.getAll(store.currentProjectId)
|
||||
if (result.success) cards.value = result.data
|
||||
}
|
||||
|
||||
// При смене проекта — перезагружаем карточки
|
||||
const onProjectChange = async () => {
|
||||
activeDepartment.value = null
|
||||
await fetchCards()
|
||||
}
|
||||
|
||||
// ==================== ФИЛЬТР ПО ОТДЕЛАМ ====================
|
||||
const savedDepartment = localStorage.getItem('activeDepartment')
|
||||
const activeDepartment = ref(savedDepartment ? parseInt(savedDepartment) : null)
|
||||
|
||||
// Сохраняем в localStorage при изменении
|
||||
watch(activeDepartment, (newVal) => {
|
||||
if (newVal === null) {
|
||||
localStorage.removeItem('activeDepartment')
|
||||
@@ -100,91 +128,60 @@ watch(activeDepartment, (newVal) => {
|
||||
localStorage.setItem('activeDepartment', newVal.toString())
|
||||
}
|
||||
})
|
||||
// Статистика для шапки
|
||||
|
||||
// ==================== СТАТИСТИКА ====================
|
||||
const stats = ref({ total: 0, inProgress: 0, done: 0 })
|
||||
// Данные из API
|
||||
const departments = ref([])
|
||||
const labels = ref([])
|
||||
const columns = ref([])
|
||||
const cards = ref([])
|
||||
const users = ref([])
|
||||
|
||||
// Загрузка всех данных из API параллельно
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const [departmentsData, labelsData, columnsData, cardsData, usersData] = await Promise.all([
|
||||
departmentsApi.getAll(),
|
||||
labelsApi.getAll(),
|
||||
columnsApi.getAll(),
|
||||
cardsApi.getAll(),
|
||||
usersApi.getAll()
|
||||
])
|
||||
|
||||
if (departmentsData.success) departments.value = departmentsData.data
|
||||
if (labelsData.success) labels.value = labelsData.data
|
||||
if (columnsData.success) columns.value = columnsData.data
|
||||
if (cardsData.success) cards.value = cardsData.data
|
||||
if (usersData.success) users.value = usersData.data
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки данных:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Выход из системы
|
||||
// Ссылка на компонент Board для вызова его методов
|
||||
// ==================== ПАНЕЛЬ РЕДАКТИРОВАНИЯ ====================
|
||||
const boardRef = ref(null)
|
||||
// Состояние панели редактирования
|
||||
const panelOpen = ref(false)
|
||||
// Редактируемая карточка (null = создание новой)
|
||||
const editingCard = ref(null)
|
||||
// ID колонки для новой/редактируемой карточки
|
||||
const editingColumnId = ref(null)
|
||||
|
||||
// Открыть панель для редактирования существующей задачи
|
||||
const openTaskPanel = ({ card, columnId }) => {
|
||||
editingCard.value = card
|
||||
editingColumnId.value = columnId
|
||||
panelOpen.value = true
|
||||
}
|
||||
|
||||
// Открыть панель для создания новой задачи
|
||||
const openNewTaskPanel = (columnId) => {
|
||||
editingCard.value = null
|
||||
editingColumnId.value = columnId
|
||||
panelOpen.value = true
|
||||
}
|
||||
|
||||
// Закрыть панель и сбросить состояние
|
||||
const closePanel = () => {
|
||||
panelOpen.value = false
|
||||
editingCard.value = null
|
||||
editingColumnId.value = null
|
||||
}
|
||||
|
||||
// Сохранить задачу через Board компонент
|
||||
const handleSaveTask = async (taskData) => {
|
||||
if (!taskData.id && store.currentProjectId) {
|
||||
taskData.id_project = store.currentProjectId
|
||||
}
|
||||
await boardRef.value?.saveTask(taskData, editingColumnId.value)
|
||||
closePanel()
|
||||
}
|
||||
|
||||
// Удалить задачу через Board компонент
|
||||
const handleDeleteTask = async (cardId) => {
|
||||
await boardRef.value?.deleteTask(cardId, editingColumnId.value)
|
||||
closePanel()
|
||||
}
|
||||
|
||||
// Архивировать задачу через Board компонент
|
||||
const handleArchiveTask = async (cardId) => {
|
||||
await boardRef.value?.archiveTask(cardId)
|
||||
closePanel()
|
||||
}
|
||||
|
||||
// Инициализация при монтировании
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
if (window.lucide) {
|
||||
window.lucide.createIcons()
|
||||
}
|
||||
// ==================== ИНИЦИАЛИЗАЦИЯ ====================
|
||||
onMounted(async () => {
|
||||
// Инициализируем store (загрузит проекты, departments, labels, users)
|
||||
await store.init()
|
||||
// Загружаем карточки
|
||||
await fetchCards()
|
||||
|
||||
if (window.lucide) window.lucide.createIcons()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -195,7 +192,6 @@ onMounted(() => {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
/* Основная область контента */
|
||||
.main-wrapper {
|
||||
flex: 1;
|
||||
@@ -213,6 +209,14 @@ onMounted(() => {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Разделитель между проектом и отделами */
|
||||
.filter-divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
/* Кнопка фильтра */
|
||||
.filter-tag {
|
||||
padding: 6px 12px;
|
||||
|
||||
Reference in New Issue
Block a user