Compare commits
2 Commits
190b4d0a5e
...
6928687982
| Author | SHA1 | Date | |
|---|---|---|---|
| 6928687982 | |||
| e8a4480747 |
@@ -133,6 +133,63 @@ if ($method === 'POST') {
|
|||||||
RestApi::response($result, $result['success'] ? 200 : 403);
|
RestApi::response($result, $result['success'] ? 200 : 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== CRUD ОТДЕЛОВ ====================
|
||||||
|
|
||||||
|
// Добавление отдела
|
||||||
|
if ($action === 'add_department') {
|
||||||
|
$project_id = $data['project_id'] ?? null;
|
||||||
|
$name = trim($data['name'] ?? '');
|
||||||
|
$color = $data['color'] ?? '#6366f1';
|
||||||
|
if (!$project_id || !$name) {
|
||||||
|
RestApi::response(['success' => false, 'errors' => ['data' => 'Укажите project_id и name']], 400);
|
||||||
|
}
|
||||||
|
$result = Project::addDepartment($project_id, $name, $color, $user_id);
|
||||||
|
RestApi::response($result, $result['success'] ? 200 : 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление отдела
|
||||||
|
if ($action === 'update_department') {
|
||||||
|
$id = $data['id'] ?? null;
|
||||||
|
$name = isset($data['name']) ? trim($data['name']) : null;
|
||||||
|
$color = $data['color'] ?? null;
|
||||||
|
if (!$id) {
|
||||||
|
RestApi::response(['success' => false, 'errors' => ['id' => 'Укажите ID отдела']], 400);
|
||||||
|
}
|
||||||
|
$result = Project::updateDepartment($id, $name, $color, $user_id);
|
||||||
|
RestApi::response($result, $result['success'] ? 200 : 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение количества задач в отделе (для подтверждения удаления)
|
||||||
|
if ($action === 'get_department_tasks_count') {
|
||||||
|
$id = $data['id'] ?? null;
|
||||||
|
if (!$id) {
|
||||||
|
RestApi::response(['success' => false, 'errors' => ['id' => 'Укажите ID отдела']], 400);
|
||||||
|
}
|
||||||
|
$count = Project::getDepartmentTasksCount($id);
|
||||||
|
RestApi::response(['success' => true, 'count' => $count]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление отдела
|
||||||
|
if ($action === 'delete_department') {
|
||||||
|
$id = $data['id'] ?? null;
|
||||||
|
if (!$id) {
|
||||||
|
RestApi::response(['success' => false, 'errors' => ['id' => 'Укажите ID отдела']], 400);
|
||||||
|
}
|
||||||
|
$result = Project::deleteDepartment($id, $user_id);
|
||||||
|
RestApi::response($result, $result['success'] ? 200 : 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление порядка отделов
|
||||||
|
if ($action === 'update_departments_order') {
|
||||||
|
$project_id = $data['project_id'] ?? null;
|
||||||
|
$ids = $data['ids'] ?? [];
|
||||||
|
if (!$project_id || empty($ids)) {
|
||||||
|
RestApi::response(['success' => false, 'errors' => ['data' => 'Укажите project_id и ids']], 400);
|
||||||
|
}
|
||||||
|
$result = Project::updateDepartmentsOrder($project_id, $ids, $user_id);
|
||||||
|
RestApi::response($result, $result['success'] ? 200 : 403);
|
||||||
|
}
|
||||||
|
|
||||||
// Метод не указан
|
// Метод не указан
|
||||||
if (!$action) {
|
if (!$action) {
|
||||||
RestApi::response(['success' => false, 'error' => 'Укажите метод'], 400);
|
RestApi::response(['success' => false, 'error' => 'Укажите метод'], 400);
|
||||||
|
|||||||
@@ -76,9 +76,11 @@ class Project extends BaseEntity {
|
|||||||
return Database::select('departments', [
|
return Database::select('departments', [
|
||||||
'id',
|
'id',
|
||||||
'name_departments',
|
'name_departments',
|
||||||
'color'
|
'color',
|
||||||
|
'order_id'
|
||||||
], [
|
], [
|
||||||
'id_project' => $project_id
|
'id_project' => $project_id,
|
||||||
|
'ORDER' => ['order_id' => 'ASC']
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,6 +368,110 @@ class Project extends BaseEntity {
|
|||||||
public static function setReadyColumn($project_id, $column_id, $user_id) {
|
public static function setReadyColumn($project_id, $column_id, $user_id) {
|
||||||
return ['success' => false, 'errors' => ['column' => 'Изменение финальной колонки запрещено. Финальная колонка всегда последняя.']];
|
return ['success' => false, 'errors' => ['column' => 'Изменение финальной колонки запрещено. Финальная колонка всегда последняя.']];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== CRUD ОТДЕЛОВ ====================
|
||||||
|
|
||||||
|
// Добавление отдела
|
||||||
|
public static function addDepartment($project_id, $name, $color, $user_id) {
|
||||||
|
if (!ProjectAccess::can($project_id, $user_id, 'manage_departments')) {
|
||||||
|
return ['success' => false, 'errors' => ['access' => 'Нет прав на управление отделами']];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Валидация имени
|
||||||
|
if (!$name || trim($name) === '') {
|
||||||
|
return ['success' => false, 'errors' => ['name' => 'Укажите название отдела']];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем максимальный order_id для проекта
|
||||||
|
$maxOrder = (int)(Database::max('departments', 'order_id', ['id_project' => $project_id]) ?? 0);
|
||||||
|
$newOrderId = $maxOrder + 1;
|
||||||
|
|
||||||
|
Database::insert('departments', [
|
||||||
|
'name_departments' => $name,
|
||||||
|
'color' => $color ?: '#6366f1',
|
||||||
|
'id_project' => $project_id,
|
||||||
|
'order_id' => $newOrderId
|
||||||
|
]);
|
||||||
|
|
||||||
|
$departmentId = Database::id();
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'id' => $departmentId,
|
||||||
|
'department' => [
|
||||||
|
'id' => $departmentId,
|
||||||
|
'name_departments' => $name,
|
||||||
|
'color' => $color ?: '#6366f1',
|
||||||
|
'order_id' => $newOrderId
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление отдела
|
||||||
|
public static function updateDepartment($id, $name, $color, $user_id) {
|
||||||
|
// Получаем отдел для проверки проекта
|
||||||
|
$department = Database::get('departments', ['id_project'], ['id' => $id]);
|
||||||
|
if (!$department) {
|
||||||
|
return ['success' => false, 'errors' => ['department' => 'Отдел не найден']];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ProjectAccess::can($department['id_project'], $user_id, 'manage_departments')) {
|
||||||
|
return ['success' => false, 'errors' => ['access' => 'Нет прав на управление отделами']];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Валидация имени (если передано)
|
||||||
|
if ($name !== null && trim($name) === '') {
|
||||||
|
return ['success' => false, 'errors' => ['name' => 'Укажите название отдела']];
|
||||||
|
}
|
||||||
|
|
||||||
|
$updateData = [];
|
||||||
|
if ($name !== null) $updateData['name_departments'] = $name;
|
||||||
|
if ($color !== null) $updateData['color'] = $color;
|
||||||
|
|
||||||
|
if (!empty($updateData)) {
|
||||||
|
Database::update('departments', $updateData, ['id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['success' => true];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение количества задач в отделе
|
||||||
|
public static function getDepartmentTasksCount($department_id) {
|
||||||
|
return Database::count('cards_task', ['id_department' => $department_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление отдела (обнуляет id_department у задач)
|
||||||
|
public static function deleteDepartment($id, $user_id) {
|
||||||
|
// Получаем отдел для проверки проекта
|
||||||
|
$department = Database::get('departments', ['id_project'], ['id' => $id]);
|
||||||
|
if (!$department) {
|
||||||
|
return ['success' => false, 'errors' => ['department' => 'Отдел не найден']];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ProjectAccess::can($department['id_project'], $user_id, 'manage_departments')) {
|
||||||
|
return ['success' => false, 'errors' => ['access' => 'Нет прав на управление отделами']];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обнуляем id_department у задач (не удаляем сами задачи)
|
||||||
|
Database::update('cards_task', ['id_department' => null], ['id_department' => $id]);
|
||||||
|
|
||||||
|
// Удаляем отдел
|
||||||
|
Database::delete('departments', ['id' => $id]);
|
||||||
|
|
||||||
|
return ['success' => true];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление порядка отделов
|
||||||
|
public static function updateDepartmentsOrder($project_id, $ids, $user_id) {
|
||||||
|
if (!ProjectAccess::can($project_id, $user_id, 'manage_departments')) {
|
||||||
|
return ['success' => false, 'errors' => ['access' => 'Нет прав на управление отделами']];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($ids as $order => $id) {
|
||||||
|
Database::update('departments', ['order_id' => $order + 1], ['id' => $id, 'id_project' => $project_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['success' => true];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -142,6 +142,42 @@ export const projectsApi = {
|
|||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ action: 'set_ready_column', project_id, column_id })
|
body: JSON.stringify({ action: 'set_ready_column', project_id, column_id })
|
||||||
|
}),
|
||||||
|
// ==================== ОТДЕЛЫ ====================
|
||||||
|
// Добавление отдела
|
||||||
|
addDepartment: (project_id, name, color) => request('/api/project', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ action: 'add_department', project_id, name, color })
|
||||||
|
}),
|
||||||
|
// Обновление отдела
|
||||||
|
updateDepartment: (id, name, color) => request('/api/project', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ action: 'update_department', id, name, color })
|
||||||
|
}),
|
||||||
|
// Получение количества задач в отделе
|
||||||
|
getDepartmentTasksCount: (id) => request('/api/project', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ action: 'get_department_tasks_count', id })
|
||||||
|
}),
|
||||||
|
// Удаление отдела
|
||||||
|
deleteDepartment: (id) => request('/api/project', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ action: 'delete_department', id })
|
||||||
|
}),
|
||||||
|
// Обновление порядка отделов
|
||||||
|
updateDepartmentsOrder: (project_id, ids) => request('/api/project', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ action: 'update_departments_order', project_id, ids })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,92 @@
|
|||||||
Добавить колонку
|
Добавить колонку
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Отделы -->
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="form-label">Отделы</label>
|
||||||
|
|
||||||
|
<div class="departments-list" v-if="form.departments.length > 0">
|
||||||
|
<div
|
||||||
|
v-for="(department, index) in form.departments"
|
||||||
|
:key="department.id || department.tempId"
|
||||||
|
class="department-item"
|
||||||
|
:class="{
|
||||||
|
'is-dragging': !isMobile && deptDragIndex === index,
|
||||||
|
'drag-over-top': !isMobile && deptDragOverIndex === index && deptDragPosition === 'top',
|
||||||
|
'drag-over-bottom': !isMobile && deptDragOverIndex === index && deptDragPosition === 'bottom'
|
||||||
|
}"
|
||||||
|
:draggable="!isMobile && canReorderDepartments"
|
||||||
|
@dragstart="!isMobile && canReorderDepartments && handleDeptDragStart($event, index)"
|
||||||
|
@dragend="!isMobile && handleDeptDragEnd()"
|
||||||
|
@dragover.prevent="!isMobile && handleDeptDragOver($event, index)"
|
||||||
|
@dragleave="!isMobile && handleDeptDragLeave()"
|
||||||
|
@drop.prevent="!isMobile && handleDeptDrop(index)"
|
||||||
|
>
|
||||||
|
<!-- Desktop: drag handle -->
|
||||||
|
<div v-if="!isMobile && canReorderDepartments" class="department-drag-handle">
|
||||||
|
<i data-lucide="grip-vertical"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile: up/down arrows -->
|
||||||
|
<div v-else-if="isMobile && canReorderDepartments" class="department-move-buttons">
|
||||||
|
<button
|
||||||
|
class="move-btn"
|
||||||
|
:disabled="index === 0"
|
||||||
|
@click="moveDepartmentUp(index)"
|
||||||
|
title="Вверх"
|
||||||
|
>
|
||||||
|
<i data-lucide="chevron-up"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="move-btn"
|
||||||
|
:disabled="index === form.departments.length - 1"
|
||||||
|
@click="moveDepartmentDown(index)"
|
||||||
|
title="Вниз"
|
||||||
|
>
|
||||||
|
<i data-lucide="chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ColorPicker v-model="department.color" />
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="department-name"
|
||||||
|
:ref="el => departmentInputRefs[index] = el"
|
||||||
|
v-model="department.name_departments"
|
||||||
|
placeholder="Название отдела"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Иконка редактирования -->
|
||||||
|
<button
|
||||||
|
class="department-edit-btn"
|
||||||
|
title="Редактировать название"
|
||||||
|
@click="focusDepartmentName(index)"
|
||||||
|
>
|
||||||
|
<i data-lucide="pencil"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Кнопка удаления -->
|
||||||
|
<button
|
||||||
|
class="department-delete-btn"
|
||||||
|
title="Удалить отдел"
|
||||||
|
@click="confirmDeleteDepartment(index)"
|
||||||
|
>
|
||||||
|
<i data-lucide="trash-2"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-else class="no-departments-hint">
|
||||||
|
Отделов пока нет. Добавьте первый отдел.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button class="add-department-btn" @click="addDepartment">
|
||||||
|
<i data-lucide="plus"></i>
|
||||||
|
Добавить отдел
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -150,6 +236,16 @@
|
|||||||
@cancel="showDeleteColumnDialog = false"
|
@cancel="showDeleteColumnDialog = false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Диалог удаления отдела -->
|
||||||
|
<ConfirmDialog
|
||||||
|
:show="showDeleteDepartmentDialog"
|
||||||
|
type="deleteDepartment"
|
||||||
|
:message="deleteDepartmentMessage"
|
||||||
|
:action="confirmDeleteDepartmentAction"
|
||||||
|
@confirm="showDeleteDepartmentDialog = false"
|
||||||
|
@cancel="showDeleteDepartmentDialog = false"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Диалог несохранённых изменений -->
|
<!-- Диалог несохранённых изменений -->
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
:show="showUnsavedDialog"
|
:show="showUnsavedDialog"
|
||||||
@@ -194,7 +290,8 @@ const isSaving = ref(false)
|
|||||||
// Form data
|
// Form data
|
||||||
const form = ref({
|
const form = ref({
|
||||||
name: '',
|
name: '',
|
||||||
columns: []
|
columns: [],
|
||||||
|
departments: []
|
||||||
})
|
})
|
||||||
|
|
||||||
// Initial form for change detection
|
// Initial form for change detection
|
||||||
@@ -203,17 +300,29 @@ const initialForm = ref(null)
|
|||||||
// Dialogs
|
// Dialogs
|
||||||
const showDeleteProjectDialog = ref(false)
|
const showDeleteProjectDialog = ref(false)
|
||||||
const showDeleteColumnDialog = ref(false)
|
const showDeleteColumnDialog = ref(false)
|
||||||
|
const showDeleteDepartmentDialog = ref(false)
|
||||||
const showUnsavedDialog = ref(false)
|
const showUnsavedDialog = ref(false)
|
||||||
const columnToDelete = ref(null)
|
const columnToDelete = ref(null)
|
||||||
const deleteColumnMessage = ref('')
|
const deleteColumnMessage = ref('')
|
||||||
|
const departmentToDelete = ref(null)
|
||||||
|
const deleteDepartmentMessage = ref('')
|
||||||
|
|
||||||
// Drag state
|
// Отделы к удалению (ID существующих отделов для удаления при сохранении)
|
||||||
|
const departmentsToDelete = ref([])
|
||||||
|
|
||||||
|
// Drag state for columns
|
||||||
const dragIndex = ref(null)
|
const dragIndex = ref(null)
|
||||||
const dragOverIndex = ref(null)
|
const dragOverIndex = ref(null)
|
||||||
const dragPosition = ref(null) // 'top' | 'bottom'
|
const dragPosition = ref(null) // 'top' | 'bottom'
|
||||||
const columnsListRef = ref(null)
|
const columnsListRef = ref(null)
|
||||||
const columnInputRefs = ref([])
|
const columnInputRefs = ref([])
|
||||||
|
|
||||||
|
// Drag state for departments
|
||||||
|
const deptDragIndex = ref(null)
|
||||||
|
const deptDragOverIndex = ref(null)
|
||||||
|
const deptDragPosition = ref(null) // 'top' | 'bottom'
|
||||||
|
const departmentInputRefs = ref([])
|
||||||
|
|
||||||
// Temp ID counter for new columns
|
// Temp ID counter for new columns
|
||||||
let tempIdCounter = 0
|
let tempIdCounter = 0
|
||||||
|
|
||||||
@@ -227,6 +336,11 @@ const canReorderColumns = computed(() => {
|
|||||||
return form.value.columns.length > 2
|
return form.value.columns.length > 2
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Можно ли сортировать отделы (больше 1 отдела)
|
||||||
|
const canReorderDepartments = computed(() => {
|
||||||
|
return form.value.departments.length > 1
|
||||||
|
})
|
||||||
|
|
||||||
// Has changes
|
// Has changes
|
||||||
const hasChanges = computed(() => {
|
const hasChanges = computed(() => {
|
||||||
if (!initialForm.value) return false
|
if (!initialForm.value) return false
|
||||||
@@ -340,6 +454,176 @@ const confirmDeleteColumnAction = async () => {
|
|||||||
columnToDelete.value = null
|
columnToDelete.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== DEPARTMENTS MANAGEMENT ====================
|
||||||
|
|
||||||
|
// Цвета для новых отделов
|
||||||
|
const departmentColors = [
|
||||||
|
'#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16',
|
||||||
|
'#22c55e', '#10b981', '#14b8a6', '#06b6d4', '#0ea5e9',
|
||||||
|
'#3b82f6', '#6366f1', '#8b5cf6', '#a855f7', '#d946ef',
|
||||||
|
'#ec4899', '#f43f5e'
|
||||||
|
]
|
||||||
|
|
||||||
|
const getRandomDepartmentColor = () => {
|
||||||
|
// Получаем цвета уже используемых отделов
|
||||||
|
const usedColors = form.value.departments.map(d => d.color)
|
||||||
|
// Фильтруем доступные цвета
|
||||||
|
const availableColors = departmentColors.filter(c => !usedColors.includes(c))
|
||||||
|
// Если все цвета использованы — берём из полного списка
|
||||||
|
const colorPool = availableColors.length > 0 ? availableColors : departmentColors
|
||||||
|
return colorPool[Math.floor(Math.random() * colorPool.length)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Фокус на поле названия отдела
|
||||||
|
const focusDepartmentName = (index) => {
|
||||||
|
const input = departmentInputRefs.value[index]
|
||||||
|
if (input) {
|
||||||
|
input.focus()
|
||||||
|
const len = input.value.length
|
||||||
|
input.setSelectionRange(len, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addDepartment = () => {
|
||||||
|
const newDepartment = {
|
||||||
|
tempId: `new-dept-${++tempIdCounter}`,
|
||||||
|
name_departments: '',
|
||||||
|
color: getRandomDepartmentColor(),
|
||||||
|
order_id: form.value.departments.length + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
form.value.departments.push(newDepartment)
|
||||||
|
nextTick(refreshIcons)
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmDeleteDepartment = async (index) => {
|
||||||
|
const department = form.value.departments[index]
|
||||||
|
|
||||||
|
// Для новых отделов (ещё не на сервере) — удаляем сразу без диалога
|
||||||
|
if (department.tempId && !department.id) {
|
||||||
|
form.value.departments.splice(index, 1)
|
||||||
|
nextTick(refreshIcons)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для существующих отделов — показываем диалог с предупреждением
|
||||||
|
departmentToDelete.value = index
|
||||||
|
|
||||||
|
const count = await store.getDepartmentTasksCount(department.id)
|
||||||
|
if (count > 0) {
|
||||||
|
deleteDepartmentMessage.value = `В отделе ${count} задач.<br>Задачи не удалятся, но потеряют привязку к отделу.`
|
||||||
|
} else {
|
||||||
|
deleteDepartmentMessage.value = 'Отдел будет удалён.'
|
||||||
|
}
|
||||||
|
|
||||||
|
showDeleteDepartmentDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmDeleteDepartmentAction = async () => {
|
||||||
|
const index = departmentToDelete.value
|
||||||
|
const department = form.value.departments[index]
|
||||||
|
|
||||||
|
// Если отдел существует на сервере — добавляем в список для удаления при сохранении
|
||||||
|
if (department.id) {
|
||||||
|
departmentsToDelete.value.push(department.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем из формы (локально)
|
||||||
|
form.value.departments.splice(index, 1)
|
||||||
|
|
||||||
|
departmentToDelete.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== MOBILE: MOVE DEPARTMENTS ====================
|
||||||
|
|
||||||
|
const moveDepartmentUp = (index) => {
|
||||||
|
if (index <= 0) return
|
||||||
|
|
||||||
|
const departments = form.value.departments
|
||||||
|
;[departments[index - 1], departments[index]] = [departments[index], departments[index - 1]]
|
||||||
|
|
||||||
|
// Update order_id
|
||||||
|
departments.forEach((dept, idx) => {
|
||||||
|
dept.order_id = idx + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
nextTick(refreshIcons)
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveDepartmentDown = (index) => {
|
||||||
|
const lastIndex = form.value.departments.length - 1
|
||||||
|
if (index >= lastIndex) return
|
||||||
|
|
||||||
|
const departments = form.value.departments
|
||||||
|
;[departments[index], departments[index + 1]] = [departments[index + 1], departments[index]]
|
||||||
|
|
||||||
|
// Update order_id
|
||||||
|
departments.forEach((dept, idx) => {
|
||||||
|
dept.order_id = idx + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
nextTick(refreshIcons)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== DRAG AND DROP DEPARTMENTS (Desktop) ====================
|
||||||
|
|
||||||
|
const handleDeptDragStart = (e, index) => {
|
||||||
|
deptDragIndex.value = index
|
||||||
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
e.dataTransfer.setData('text/plain', `dept-${index}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeptDragEnd = () => {
|
||||||
|
deptDragIndex.value = null
|
||||||
|
deptDragOverIndex.value = null
|
||||||
|
deptDragPosition.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeptDragOver = (e, index) => {
|
||||||
|
if (deptDragIndex.value === null || deptDragIndex.value === index) return
|
||||||
|
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect()
|
||||||
|
const midY = rect.top + rect.height / 2
|
||||||
|
|
||||||
|
deptDragOverIndex.value = index
|
||||||
|
deptDragPosition.value = e.clientY < midY ? 'top' : 'bottom'
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeptDragLeave = () => {
|
||||||
|
// Don't clear immediately to avoid flickering
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeptDrop = (targetIndex) => {
|
||||||
|
if (deptDragIndex.value === null || deptDragIndex.value === targetIndex) {
|
||||||
|
handleDeptDragEnd()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceIndex = deptDragIndex.value
|
||||||
|
let insertIndex = targetIndex
|
||||||
|
|
||||||
|
// Adjust insert index based on drag position
|
||||||
|
if (deptDragPosition.value === 'bottom') {
|
||||||
|
insertIndex = targetIndex + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// If moving down, adjust for removal
|
||||||
|
if (sourceIndex < insertIndex) {
|
||||||
|
insertIndex--
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reorder array
|
||||||
|
const [movedDept] = form.value.departments.splice(sourceIndex, 1)
|
||||||
|
form.value.departments.splice(insertIndex, 0, movedDept)
|
||||||
|
|
||||||
|
// Update order_id
|
||||||
|
form.value.departments.forEach((dept, idx) => {
|
||||||
|
dept.order_id = idx + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
handleDeptDragEnd()
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== MOBILE: MOVE COLUMNS ====================
|
// ==================== MOBILE: MOVE COLUMNS ====================
|
||||||
|
|
||||||
const moveColumnUp = async (index) => {
|
const moveColumnUp = async (index) => {
|
||||||
@@ -488,15 +772,35 @@ const handleSave = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isNew.value) {
|
if (isNew.value) {
|
||||||
|
// Проверяем что у всех отделов есть имена (для нового проекта тоже)
|
||||||
|
const emptyDeptNew = form.value.departments.find(d => !d.name_departments?.trim())
|
||||||
|
if (emptyDeptNew) {
|
||||||
|
toast.error('Укажите название для всех отделов')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Создание проекта
|
// Создание проекта
|
||||||
const result = await store.createProject(form.value.name)
|
const result = await store.createProject(form.value.name)
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// Обновляем колонки если есть изменения
|
const newProjectId = result.id
|
||||||
// Дефолтные колонки уже созданы сервером
|
|
||||||
// Здесь можно добавить логику для кастомизации колонок при создании
|
// Создаём отделы для нового проекта
|
||||||
|
for (const department of form.value.departments) {
|
||||||
|
if (department.tempId) {
|
||||||
|
const deptResult = await store.addDepartmentToProject(
|
||||||
|
newProjectId,
|
||||||
|
department.name_departments,
|
||||||
|
department.color
|
||||||
|
)
|
||||||
|
if (!deptResult.success) {
|
||||||
|
toast.error(deptResult.errors?.name || 'Ошибка создания отдела')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toast.success('Проект создан')
|
toast.success('Проект создан')
|
||||||
emit('saved', result.id)
|
emit('saved', newProjectId)
|
||||||
emit('close')
|
emit('close')
|
||||||
} else {
|
} else {
|
||||||
toast.error('Ошибка создания проекта')
|
toast.error('Ошибка создания проекта')
|
||||||
@@ -536,6 +840,58 @@ const handleSave = async () => {
|
|||||||
await store.reorderColumns(ids)
|
await store.reorderColumns(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Удаляем отделы, помеченные на удаление
|
||||||
|
for (const deptId of departmentsToDelete.value) {
|
||||||
|
const result = await store.deleteDepartment(deptId)
|
||||||
|
if (!result.success) {
|
||||||
|
toast.error(result.errors?.department || result.errors?.access || 'Ошибка удаления отдела')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
departmentsToDelete.value = []
|
||||||
|
|
||||||
|
// Проверяем что у всех отделов есть имена
|
||||||
|
const emptyDepartment = form.value.departments.find(d => !d.name_departments?.trim())
|
||||||
|
if (emptyDepartment) {
|
||||||
|
toast.error('Укажите название для всех отделов')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обрабатываем отделы
|
||||||
|
for (const department of form.value.departments) {
|
||||||
|
if (department.tempId && !department.id) {
|
||||||
|
// Новый отдел
|
||||||
|
const result = await store.addDepartment(department.name_departments, department.color)
|
||||||
|
if (result.success) {
|
||||||
|
department.id = result.id
|
||||||
|
delete department.tempId
|
||||||
|
} else {
|
||||||
|
toast.error(result.errors?.name || result.errors?.access || 'Ошибка создания отдела')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if (department.id) {
|
||||||
|
// Проверяем изменения в существующем отделе
|
||||||
|
const original = initialForm.value.departments.find(d => d.id === department.id)
|
||||||
|
if (original) {
|
||||||
|
const nameChanged = department.name_departments !== original.name_departments
|
||||||
|
const colorChanged = department.color !== original.color
|
||||||
|
if (nameChanged || colorChanged) {
|
||||||
|
const result = await store.updateDepartment(department.id, department.name_departments, department.color)
|
||||||
|
if (!result.success) {
|
||||||
|
toast.error(result.errors?.name || result.errors?.access || 'Ошибка обновления отдела')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраняем порядок отделов
|
||||||
|
const deptIds = form.value.departments.filter(d => d.id).map(d => d.id)
|
||||||
|
if (deptIds.length > 0) {
|
||||||
|
await store.reorderDepartments(deptIds)
|
||||||
|
}
|
||||||
|
|
||||||
toast.success('Изменения сохранены')
|
toast.success('Изменения сохранены')
|
||||||
emit('saved', props.project.id)
|
emit('saved', props.project.id)
|
||||||
emit('close')
|
emit('close')
|
||||||
@@ -595,12 +951,14 @@ watch(() => props.show, async (newVal) => {
|
|||||||
if (newVal) {
|
if (newVal) {
|
||||||
isSaving.value = false
|
isSaving.value = false
|
||||||
tempIdCounter = 0
|
tempIdCounter = 0
|
||||||
|
departmentsToDelete.value = []
|
||||||
|
|
||||||
if (props.project) {
|
if (props.project) {
|
||||||
// Редактирование — загружаем данные
|
// Редактирование — загружаем данные
|
||||||
form.value = {
|
form.value = {
|
||||||
name: props.project.name,
|
name: props.project.name,
|
||||||
columns: JSON.parse(JSON.stringify(store.columns))
|
columns: JSON.parse(JSON.stringify(store.columns)),
|
||||||
|
departments: JSON.parse(JSON.stringify(store.departments))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Создание — дефолтные значения (последняя колонка = финальная)
|
// Создание — дефолтные значения (последняя колонка = финальная)
|
||||||
@@ -609,6 +967,9 @@ watch(() => props.show, async (newVal) => {
|
|||||||
columns: [
|
columns: [
|
||||||
{ tempId: 'new-1', name_columns: 'К выполнению', color: '#6366f1', id_order: 1 },
|
{ tempId: 'new-1', name_columns: 'К выполнению', color: '#6366f1', id_order: 1 },
|
||||||
{ tempId: 'new-2', name_columns: 'Готово', color: '#22c55e', id_order: 2 }
|
{ tempId: 'new-2', name_columns: 'Готово', color: '#22c55e', id_order: 2 }
|
||||||
|
],
|
||||||
|
departments: [
|
||||||
|
{ tempId: 'new-dept-1', name_departments: 'Отдел разработки', color: '#3b82f6', order_id: 1 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
tempIdCounter = 2
|
tempIdCounter = 2
|
||||||
@@ -621,10 +982,14 @@ watch(() => props.show, async (newVal) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Refresh icons when columns change
|
// Refresh icons when columns or departments change
|
||||||
watch(() => form.value.columns.length, () => {
|
watch(() => form.value.columns.length, () => {
|
||||||
nextTick(refreshIcons)
|
nextTick(refreshIcons)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(() => form.value.departments.length, () => {
|
||||||
|
nextTick(refreshIcons)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -862,6 +1227,182 @@ watch(() => form.value.columns.length, () => {
|
|||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================== DEPARTMENTS LIST ==================== */
|
||||||
|
|
||||||
|
.departments-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-item.is-dragging {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-item.drag-over-top {
|
||||||
|
border-top: 2px solid var(--accent);
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-item.drag-over-bottom {
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-drag-handle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: grab;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-drag-handle:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-drag-handle i {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: up/down buttons for departments */
|
||||||
|
.department-move-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-name {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-name::placeholder {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-name:focus {
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-edit-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-edit-btn i {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-edit-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-delete-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-delete-btn i {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-delete-btn:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-departments-hint {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== ADD DEPARTMENT BUTTON ==================== */
|
||||||
|
|
||||||
|
.add-department-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px dashed rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-department-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-department-btn i {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ==================== FOOTER ==================== */
|
/* ==================== FOOTER ==================== */
|
||||||
|
|
||||||
.footer-left {
|
.footer-left {
|
||||||
|
|||||||
@@ -98,5 +98,13 @@ export const DIALOGS = {
|
|||||||
message: 'Вы будете перенаправлены<br>на страницу входа.',
|
message: 'Вы будете перенаправлены<br>на страницу входа.',
|
||||||
confirmText: 'Выйти',
|
confirmText: 'Выйти',
|
||||||
variant: 'warning'
|
variant: 'warning'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Удаление отдела
|
||||||
|
deleteDepartment: {
|
||||||
|
title: 'Удалить отдел?',
|
||||||
|
message: '', // Будет задан динамически
|
||||||
|
confirmText: 'Удалить',
|
||||||
|
variant: 'danger'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -596,6 +596,78 @@ export const useProjectsStore = defineStore('projects', () => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== CRUD ОТДЕЛОВ ====================
|
||||||
|
|
||||||
|
// Добавление отдела
|
||||||
|
const addDepartment = async (name, color = '#6366f1') => {
|
||||||
|
if (!currentProjectId.value) return { success: false }
|
||||||
|
|
||||||
|
const result = await projectsApi.addDepartment(currentProjectId.value, name, color)
|
||||||
|
if (result.success) {
|
||||||
|
departments.value.push(result.department)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление отдела
|
||||||
|
const updateDepartment = async (id, name, color) => {
|
||||||
|
const result = await projectsApi.updateDepartment(id, name, color)
|
||||||
|
if (result.success) {
|
||||||
|
const department = departments.value.find(d => d.id === id)
|
||||||
|
if (department) {
|
||||||
|
if (name !== null && name !== undefined) department.name_departments = name
|
||||||
|
if (color !== null && color !== undefined) department.color = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение количества задач в отделе
|
||||||
|
const getDepartmentTasksCount = async (id) => {
|
||||||
|
const result = await projectsApi.getDepartmentTasksCount(id)
|
||||||
|
return result.success ? result.count : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление отдела
|
||||||
|
const deleteDepartment = async (id) => {
|
||||||
|
const result = await projectsApi.deleteDepartment(id)
|
||||||
|
if (result.success) {
|
||||||
|
const index = departments.value.findIndex(d => d.id === id)
|
||||||
|
if (index !== -1) {
|
||||||
|
departments.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
// Обнуляем id_department у карточек этого отдела
|
||||||
|
cards.value.forEach(card => {
|
||||||
|
if (card.id_department === id) {
|
||||||
|
card.id_department = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление порядка отделов
|
||||||
|
const reorderDepartments = async (ids) => {
|
||||||
|
if (!currentProjectId.value) return
|
||||||
|
|
||||||
|
// Оптимистичное обновление
|
||||||
|
const reordered = ids.map((id, index) => {
|
||||||
|
const department = departments.value.find(d => d.id === id)
|
||||||
|
return { ...department, order_id: index + 1 }
|
||||||
|
})
|
||||||
|
departments.value = reordered
|
||||||
|
|
||||||
|
// Отправляем на сервер
|
||||||
|
await projectsApi.updateDepartmentsOrder(currentProjectId.value, ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавление отдела в конкретный проект (для создания нового проекта)
|
||||||
|
const addDepartmentToProject = async (projectId, name, color = '#6366f1') => {
|
||||||
|
const result = await projectsApi.addDepartment(projectId, name, color)
|
||||||
|
// Не добавляем в локальный state — при переключении на проект данные загрузятся
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Состояние
|
// Состояние
|
||||||
projects,
|
projects,
|
||||||
@@ -646,6 +718,13 @@ export const useProjectsStore = defineStore('projects', () => {
|
|||||||
getColumnTasksCount,
|
getColumnTasksCount,
|
||||||
deleteColumn,
|
deleteColumn,
|
||||||
reorderColumns,
|
reorderColumns,
|
||||||
setReadyColumn
|
setReadyColumn,
|
||||||
|
// CRUD отделов
|
||||||
|
addDepartment,
|
||||||
|
addDepartmentToProject,
|
||||||
|
updateDepartment,
|
||||||
|
getDepartmentTasksCount,
|
||||||
|
deleteDepartment,
|
||||||
|
reorderDepartments
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -375,7 +375,9 @@ const handleLogin = async () => {
|
|||||||
const data = await authApi.login(login.value, password.value)
|
const data = await authApi.login(login.value, password.value)
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
setAuthCache(true)
|
// Получаем данные пользователя для кэша
|
||||||
|
const checkResult = await authApi.check()
|
||||||
|
setAuthCache(true, checkResult.user || null)
|
||||||
showSuccess.value = true
|
showSuccess.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
refreshIcons()
|
refreshIcons()
|
||||||
@@ -421,7 +423,9 @@ const handleRegister = async () => {
|
|||||||
const loginResult = await authApi.login(regUsername.value.trim(), regPassword.value)
|
const loginResult = await authApi.login(regUsername.value.trim(), regPassword.value)
|
||||||
|
|
||||||
if (loginResult.success) {
|
if (loginResult.success) {
|
||||||
setAuthCache(true)
|
// Получаем данные пользователя для кэша
|
||||||
|
const checkResult = await authApi.check()
|
||||||
|
setAuthCache(true, checkResult.user || null)
|
||||||
showSuccess.value = true
|
showSuccess.value = true
|
||||||
await nextTick()
|
await nextTick()
|
||||||
refreshIcons()
|
refreshIcons()
|
||||||
|
|||||||
Reference in New Issue
Block a user