| | | 1 | | @{ |
| | 0 | 2 | | ViewData["Title"] = "Категории"; |
| | | 3 | | } |
| | 0 | 4 | | @Html.AntiForgeryToken() |
| | | 5 | | <link href="~/css/tree-cards.css" rel="stylesheet" /> |
| | | 6 | | |
| | | 7 | | <div class="container"> |
| | | 8 | | <!-- Панель действий --> |
| | | 9 | | <div class="action-panel"> |
| | | 10 | | <a href="#" class="action-link" id="clear-selection">Очистить выбор</a> |
| | | 11 | | <button id="confirmLinkButton" class="action-button" onclick="sendCategoriesToServer()">Связать категории</butto |
| | | 12 | | <div class="translation-section" id="translation-section-id"> |
| | | 13 | | <input type="text" id="translationInput" class="translation-input" placeholder="Введите перевод"> |
| | | 14 | | <button id="sendTranslationButton" class="action-button" onclick="sendTranslationToServer()">Добавить перево |
| | | 15 | | </div> |
| | | 16 | | <a href="#" class="action-link" id="settings-button" onclick="showSettingsModal()">Настройки отображения</a> |
| | | 17 | | <a asp-area="OzonCards" asp-action="Index" asp-controller="Tasks">Задачи</a> |
| | | 18 | | </div> |
| | | 19 | | |
| | | 20 | | <!-- Основной контейнер с колонками --> |
| | | 21 | | <div class="row" id="columns-container"> |
| | | 22 | | <!-- Правая колонка (Ct) --> |
| | | 23 | | <div class="col-md-4 tree-container main-column" id="ct-column"> |
| | | 24 | | <h4 class="text-center">Ct (Главная)</h4> |
| | | 25 | | <input type="text" id="ct-filter" class="form-control filter-input" placeholder="Фильтр категорий"> |
| | | 26 | | <div id="ct-category-container" class="tree-scroll"> |
| | | 27 | | <p>Загрузка...</p> |
| | | 28 | | </div> |
| | | 29 | | </div> |
| | | 30 | | |
| | | 31 | | <!-- Левая колонка (Volna) --> |
| | | 32 | | <div class="col-md-4 tree-container" id="volna-column"> |
| | | 33 | | <h4 class="text-center">Volna</h4> |
| | | 34 | | <input type="text" id="volna-filter" class="form-control filter-input" placeholder="Фильтр категорий"> |
| | | 35 | | <div id="volna-category-container" class="tree-scroll"> |
| | | 36 | | <p>Загрузка...</p> |
| | | 37 | | </div> |
| | | 38 | | </div> |
| | | 39 | | |
| | | 40 | | <!-- Центральная колонка (Ozon) --> |
| | | 41 | | <div class="col-md-4 tree-container" id="ozon-column"> |
| | | 42 | | <h4 class="text-center">Ozon</h4> |
| | | 43 | | <input type="text" id="ozon-filter" class="form-control filter-input" placeholder="Фильтр категорий"> |
| | | 44 | | <div id="ozon-category-container" class="tree-scroll"> |
| | | 45 | | <p>Загрузка...</p> |
| | | 46 | | </div> |
| | | 47 | | </div> |
| | | 48 | | |
| | | 49 | | <!-- Колонка Метод вызова и фильтрации --> |
| | | 50 | | <div class="col-md-4 tree-container" id="method-column"> |
| | | 51 | | <h4 class="text-center">IC</h4> |
| | | 52 | | <input type="text" id="method-filter" class="form-control filter-input" placeholder="Фильтр методов"> |
| | | 53 | | <div id="method-category-container" class="tree-scroll"> |
| | | 54 | | <p>Загрузка...</p> |
| | | 55 | | </div> |
| | | 56 | | </div> |
| | | 57 | | |
| | | 58 | | |
| | | 59 | | <div class="col-md-4 tree-container" id="cattree-column"> |
| | | 60 | | <h4 class="text-center">CatTree</h4> |
| | | 61 | | <input type="text" id="cattree-filter" class="form-control filter-input" placeholder="Фильтр категорий"> |
| | | 62 | | <div id="cattree-category-container" class="tree-scroll"> |
| | | 63 | | <p>Загрузка...</p> |
| | | 64 | | </div> |
| | | 65 | | </div> |
| | | 66 | | </div> |
| | | 67 | | </div> |
| | | 68 | | |
| | | 69 | | <!-- Модальное окно настроек --> |
| | | 70 | | <div id="settings-modal" class="modal"> |
| | | 71 | | <div class="modal-content"> |
| | | 72 | | <span class="close" onclick="hideSettingsModal()">×</span> |
| | | 73 | | <h3>Настройки отображения</h3> |
| | | 74 | | <div class="settings-options"> |
| | | 75 | | <button onclick="toggleColumn('volna-column')">Скрыть/показать Volna</button> |
| | | 76 | | <button onclick="toggleColumn('ozon-column')">Скрыть/показать Ozon</button> |
| | | 77 | | <button onclick="toggleColumn('method-column')">Скрыть/показать Метод вызова и фильтрации</button> |
| | | 78 | | <button onclick="toggleColumn('cattree-column')">Скрыть/показать CatTree</button> |
| | | 79 | | </div> |
| | | 80 | | </div> |
| | | 81 | | </div> |
| | | 82 | | |
| | | 83 | | <script> |
| | | 84 | | async function unlinkCategory(id) { |
| | | 85 | | if (await St2Dialog.confirm("Вы уверены, что хотите отвязать категорию?", { variant: 'danger', confirmText: 'Отв |
| | | 86 | | const token = document.querySelector('input[name="__RequestVerificationToken"]').value; |
| | | 87 | | |
| | | 88 | | fetch(`CategoryTree/Category/Unlink?id=${id}`, { |
| | | 89 | | method: 'POST', |
| | | 90 | | headers: { |
| | | 91 | | 'Content-Type': 'application/json', |
| | | 92 | | 'RequestVerificationToken': token |
| | | 93 | | }, |
| | | 94 | | body: JSON.stringify({}) |
| | | 95 | | }) |
| | | 96 | | .then(response => { |
| | | 97 | | if (response.ok) { |
| | | 98 | | location.reload(); |
| | | 99 | | } else { |
| | | 100 | | alert("Ошибка при отвязке."); |
| | | 101 | | } |
| | | 102 | | }); |
| | | 103 | | } |
| | | 104 | | } |
| | | 105 | | |
| | | 106 | | |
| | | 107 | | // Клик по связанной категории |
| | | 108 | | $(document).on("click", ".link-item", function () { |
| | | 109 | | const type = $(this).data("type"); |
| | | 110 | | const id = $(this).data("id"); |
| | | 111 | | |
| | | 112 | | if (!type || !id) return; |
| | | 113 | | |
| | | 114 | | switch (type) { |
| | | 115 | | case "Ozon": |
| | 0 | 116 | | loadCategories("ozon-category-container", '@Url.Action("GetOzonCategoriesById", "Category")' + '?id=' + |
| | | 117 | | break; |
| | | 118 | | case "Volna": |
| | 0 | 119 | | loadCategories("volna-category-container", '@Url.Action("GetVolnaCategoriesById", "Category")' + '?id=' |
| | | 120 | | break; |
| | | 121 | | case "Ct": |
| | 0 | 122 | | loadCategories("ct-category-container", '@Url.Action("GetCtCategoriesById", "Category")' + '?id=' + id); |
| | | 123 | | break; |
| | | 124 | | case "CatTree": |
| | 0 | 125 | | loadCategories("cattree-category-container", '@Url.Action("GetCatTreeCategoriesById", "Category")' + '?i |
| | | 126 | | break; |
| | | 127 | | case "IC": |
| | 0 | 128 | | loadCategories("method-category-container", '@Url.Action("GetICGroupsCategoriesById", "Category")' + '?i |
| | | 129 | | break; |
| | | 130 | | } |
| | | 131 | | }); |
| | | 132 | | </script> |
| | | 133 | | |
| | | 134 | | |
| | | 135 | | <!-- jQuery для AJAX --> |
| | | 136 | | <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> |
| | | 137 | | |
| | | 138 | | <script> |
| | | 139 | | function showSettingsModal() { |
| | | 140 | | document.getElementById('settings-modal').style.display = 'block'; |
| | | 141 | | } |
| | | 142 | | |
| | | 143 | | function hideSettingsModal() { |
| | | 144 | | document.getElementById('settings-modal').style.display = 'none'; |
| | | 145 | | } |
| | | 146 | | |
| | | 147 | | // Закрытие модального окна при клике вне его |
| | | 148 | | window.onclick = function(event) { |
| | | 149 | | const modal = document.getElementById('settings-modal'); |
| | | 150 | | if (event.target == modal) { |
| | | 151 | | hideSettingsModal(); |
| | | 152 | | } |
| | | 153 | | } |
| | | 154 | | |
| | | 155 | | // Ваша существующая функция toggleColumn |
| | | 156 | | function toggleColumn(columnId) { |
| | | 157 | | const column = document.getElementById(columnId); |
| | | 158 | | if (column.style.display === 'none') { |
| | | 159 | | column.style.display = 'block'; |
| | | 160 | | } else { |
| | | 161 | | column.style.display = 'none'; |
| | | 162 | | } |
| | | 163 | | } |
| | | 164 | | </script> |
| | | 165 | | |
| | | 166 | | <script> |
| | | 167 | | // Функция для скрытия/показа колонок |
| | | 168 | | function toggleColumn(columnId) { |
| | | 169 | | var column = document.getElementById(columnId); |
| | | 170 | | column.classList.toggle('hidden'); |
| | | 171 | | saveColumnsState(); |
| | | 172 | | } |
| | | 173 | | |
| | | 174 | | // Функция для перемещения колонок |
| | | 175 | | function makeColumnsDraggable() { |
| | | 176 | | const columns = document.querySelectorAll('.tree-container'); |
| | | 177 | | |
| | | 178 | | columns.forEach(function(column) { |
| | | 179 | | column.setAttribute('draggable', true); |
| | | 180 | | |
| | | 181 | | column.addEventListener('dragstart', function(e) { |
| | | 182 | | e.dataTransfer.setData('text', column.id); |
| | | 183 | | }); |
| | | 184 | | |
| | | 185 | | column.addEventListener('dragover', function(e) { |
| | | 186 | | e.preventDefault(); |
| | | 187 | | }); |
| | | 188 | | |
| | | 189 | | column.addEventListener('drop', function(e) { |
| | | 190 | | e.preventDefault(); |
| | | 191 | | var draggedColumnId = e.dataTransfer.getData('text'); |
| | | 192 | | var draggedColumn = document.getElementById(draggedColumnId); |
| | | 193 | | var currentColumn = column; |
| | | 194 | | |
| | | 195 | | // Перемещаем столбцы |
| | | 196 | | if (draggedColumn !== currentColumn) { |
| | | 197 | | var parent = draggedColumn.parentNode; |
| | | 198 | | parent.insertBefore(draggedColumn, currentColumn); |
| | | 199 | | saveColumnsState(); |
| | | 200 | | } |
| | | 201 | | }); |
| | | 202 | | }); |
| | | 203 | | } |
| | | 204 | | |
| | | 205 | | // Сохранение состояния колонок в sessionStorage |
| | | 206 | | function saveColumnsState() { |
| | | 207 | | const columnsState = { |
| | | 208 | | volnaColumnHidden: document.getElementById('volna-column').classList.contains('hidden'), |
| | | 209 | | ozonColumnHidden: document.getElementById('ozon-column').classList.contains('hidden'), |
| | | 210 | | cattreeColumnHidden: document.getElementById('cattree-column').classList.contains('hidden'), |
| | | 211 | | columnsOrder: Array.from(document.getElementById('columns-container').children).map(col => col.id), |
| | | 212 | | methodColumnHidden: document.getElementById('method-column').classList.contains('hidden'), |
| | | 213 | | |
| | | 214 | | }; |
| | | 215 | | |
| | | 216 | | sessionStorage.setItem('columnsState', JSON.stringify(columnsState)); |
| | | 217 | | } |
| | | 218 | | |
| | | 219 | | // Восстановление состояния колонок из sessionStorage |
| | | 220 | | function restoreColumnsState() { |
| | | 221 | | const columnsState = JSON.parse(sessionStorage.getItem('columnsState')); |
| | | 222 | | if (columnsState) { |
| | | 223 | | // Восстанавливаем видимость колонок |
| | | 224 | | if (columnsState.volnaColumnHidden) document.getElementById('volna-column').classList.add('hidden'); |
| | | 225 | | if (columnsState.ozonColumnHidden) document.getElementById('ozon-column').classList.add('hidden'); |
| | | 226 | | if (columnsState.cattreeColumnHidden) document.getElementById('cattree-column').classList.add('hidden'); |
| | | 227 | | if (columnsState.methodColumnHidden) document.getElementById('method-column').classList.add('hidden'); |
| | | 228 | | |
| | | 229 | | // Восстанавливаем порядок колонок |
| | | 230 | | const container = document.getElementById('columns-container'); |
| | | 231 | | columnsState.columnsOrder.forEach(columnId => { |
| | | 232 | | const column = document.getElementById(columnId); |
| | | 233 | | container.appendChild(column); |
| | | 234 | | }); |
| | | 235 | | } |
| | | 236 | | } |
| | | 237 | | |
| | | 238 | | // Инициализация перемещения колонок и восстановления состояния при загрузке страницы |
| | | 239 | | window.onload = function() { |
| | | 240 | | makeColumnsDraggable(); |
| | | 241 | | restoreColumnsState(); |
| | | 242 | | }; |
| | | 243 | | </script> |
| | | 244 | | |
| | | 245 | | <script> |
| | | 246 | | // Кнопка очистки выбора |
| | | 247 | | document.getElementById("clear-selection").addEventListener("click", function () { |
| | | 248 | | sessionStorage.removeItem("selectedCategories"); |
| | | 249 | | document.querySelectorAll(".category-link").forEach(link => { |
| | | 250 | | link.style.color = ""; |
| | | 251 | | }); |
| | | 252 | | alert("Выбор категорий очищен!"); |
| | | 253 | | }); |
| | | 254 | | |
| | | 255 | | function loadCategories(containerId, action, filterValue) { |
| | | 256 | | $.get(action, { name: filterValue }, function (data) { |
| | | 257 | | $("#" + containerId).html(data); |
| | | 258 | | }); |
| | | 259 | | } |
| | | 260 | | |
| | | 261 | | $(document).ready(function () { |
| | | 262 | | // Изначальная загрузка деревьев |
| | | 263 | | // Пример вызовов из JavaScript: |
| | 0 | 264 | | loadCategories("volna-category-container", '@Url.Action("UpdateVolnaCategories", "Category", new { area = "Cate |
| | 0 | 265 | | loadCategories("ozon-category-container", '@Url.Action("UpdateOzonCategories", "Category", new { area = "Catego |
| | 0 | 266 | | loadCategories("ct-category-container", '@Url.Action("UpdateCtCategories", "Category", new { area = "CategoryTr |
| | 0 | 267 | | loadCategories("cattree-category-container", '@Url.Action("UpdateCatTreeCategories", "Category", new { area = " |
| | 0 | 268 | | loadCategories("method-category-container", '@Url.Action("UpdateICGroupsCategories", "Category", new { area = " |
| | | 269 | | |
| | | 270 | | |
| | | 271 | | // Фильтрация при вводе текста |
| | | 272 | | $(".filter-input").on("input", function () { |
| | | 273 | | let id = $(this).attr("id"); |
| | | 274 | | let value = $(this).val(); |
| | | 275 | | |
| | | 276 | | if (id === "volna-filter") { |
| | 0 | 277 | | loadCategories("volna-category-container", '@Url.Action("GetVolnaCategories", "Category", new { area = |
| | | 278 | | } else if (id === "ozon-filter") { |
| | 0 | 279 | | loadCategories("ozon-category-container", '@Url.Action("GetOzonCategories", "Category", new { area = "C |
| | | 280 | | } else if (id === "ct-filter") { |
| | 0 | 281 | | loadCategories("ct-category-container", '@Url.Action("GetCtCategories", "Category", new { area = "Categ |
| | | 282 | | } else if (id === "cattree-filter") { // Обработчик фильтра для нового дерева |
| | 0 | 283 | | loadCategories("cattree-category-container", '@Url.Action("GetCatTreeCategories", "Category", new { are |
| | | 284 | | } |
| | | 285 | | else if (id === "method-filter") { |
| | 0 | 286 | | loadCategories("method-category-container", '@Url.Action("GetICGroupsCategories", "Category", new { are |
| | | 287 | | } |
| | | 288 | | |
| | | 289 | | }); |
| | | 290 | | }); |
| | | 291 | | |
| | | 292 | | </script> |
| | | 293 | | |
| | | 294 | | <script> |
| | | 295 | | document.addEventListener("DOMContentLoaded", function () { |
| | | 296 | | let selectedData = JSON.parse(sessionStorage.getItem("selectedCategories")) || {}; |
| | | 297 | | |
| | | 298 | | document.querySelectorAll(".category-link").forEach(link => { |
| | | 299 | | let id = link.getAttribute("data-id"); |
| | | 300 | | let tree = link.getAttribute("data-tree"); |
| | | 301 | | |
| | | 302 | | if (selectedData[tree] && selectedData[tree] === id) { |
| | | 303 | | link.style.color = "red"; |
| | | 304 | | } |
| | | 305 | | }); |
| | | 306 | | }); |
| | | 307 | | |
| | | 308 | | function handleLeftClick(event, element) { |
| | | 309 | | event.preventDefault(); |
| | | 310 | | let id = element.getAttribute("data-id"); |
| | | 311 | | let tree = element.getAttribute("data-tree"); |
| | | 312 | | |
| | | 313 | | let selectedData = JSON.parse(sessionStorage.getItem("selectedCategories")) || {}; |
| | | 314 | | |
| | | 315 | | // Если уже выбрана категория для данного tree, заменяем на новую |
| | | 316 | | if (selectedData[tree] === id) { |
| | | 317 | | delete selectedData[tree]; // Удаляем, если кликнули повторно (отмена выбора) |
| | | 318 | | element.style.color = ""; |
| | | 319 | | // Проверяем, если выбрано ровно 3 категории - показываем окно |
| | | 320 | | if (Object.keys(selectedData).length < 2) { |
| | | 321 | | let button = document.getElementById("confirmLinkButton"); |
| | | 322 | | button.style.display = "none"; // Скрываем кнопку |
| | | 323 | | } |
| | | 324 | | if (Object.keys(selectedData).length === 0) { |
| | | 325 | | let translation_section = document.getElementById("translation-section-id") |
| | | 326 | | translation_section.style.display = "none"; |
| | | 327 | | } |
| | | 328 | | } else { |
| | | 329 | | // Сбрасываем цвет у старого элемента (если он был выделен) |
| | | 330 | | document.querySelectorAll(`.category-link[data-tree="${tree}"]`).forEach(link => { |
| | | 331 | | link.style.color = ""; |
| | | 332 | | }); |
| | | 333 | | |
| | | 334 | | selectedData[tree] = id; // Добавляем новую категорию |
| | | 335 | | element.style.color = "red"; |
| | | 336 | | } |
| | | 337 | | |
| | | 338 | | sessionStorage.setItem("selectedCategories", JSON.stringify(selectedData)); |
| | | 339 | | console.log("Выбранные категории:", selectedData); |
| | | 340 | | |
| | | 341 | | // Проверяем, если выбрано ровно 3 категории - показываем окно |
| | | 342 | | if (Object.keys(selectedData).length > 1) { |
| | | 343 | | showConfirmationDialog(); |
| | | 344 | | } |
| | | 345 | | if (Object.keys(selectedData).length >= 1) { |
| | | 346 | | showSection(); |
| | | 347 | | } |
| | | 348 | | console.log(sessionStorage.getItem("selectedCategories")); |
| | | 349 | | } |
| | | 350 | | |
| | 0 | 351 | | const ozonCardsBaseUrl = '@Url.Action("Index", "Cards", new { area = "OzonCards" })'; |
| | | 352 | | |
| | | 353 | | function handleRightClick(event, element) { |
| | | 354 | | event.preventDefault(); |
| | | 355 | | const encodedName = encodeURIComponent(element.dataset.name); |
| | | 356 | | window.open(`${ozonCardsBaseUrl}?typeName=${encodedName}`, '_blank'); |
| | | 357 | | return false; |
| | | 358 | | } |
| | | 359 | | |
| | | 360 | | |
| | | 361 | | // Функция для показа окна подтверждения |
| | | 362 | | function showConfirmationDialog() { |
| | | 363 | | let button = document.getElementById("confirmLinkButton"); |
| | | 364 | | button.style.display = "block"; // Показываем кнопку |
| | | 365 | | } |
| | | 366 | | function showSection() { |
| | | 367 | | let translation_section = document.getElementById("translation-section-id"); |
| | | 368 | | translation_section.style.display = "flex"; |
| | | 369 | | } |
| | | 370 | | |
| | | 371 | | function sendTranslationToServer() { |
| | | 372 | | const translationText = document.getElementById('translationInput').value.trim(); |
| | | 373 | | |
| | | 374 | | let selectedData = JSON.parse(sessionStorage.getItem("selectedCategories")) || {}; |
| | | 375 | | |
| | | 376 | | if (Object.keys(selectedData).length === 0) { |
| | | 377 | | alert('Пожалуйста, сначала выберите категории'); |
| | | 378 | | return; |
| | | 379 | | } |
| | | 380 | | |
| | | 381 | | const requestData = { |
| | | 382 | | CategoryNames: selectedData, |
| | | 383 | | Translation: translationText |
| | | 384 | | }; |
| | | 385 | | |
| | | 386 | | fetch('CategoryTree/Category/CreateCategoryTranslation', { |
| | | 387 | | method: 'POST', |
| | | 388 | | headers: { |
| | | 389 | | 'Content-Type': 'application/json' |
| | | 390 | | }, |
| | | 391 | | body: JSON.stringify(requestData) |
| | | 392 | | }) |
| | | 393 | | .then(response => { |
| | | 394 | | if (!response.ok) { |
| | | 395 | | return response.json().then(err => { throw new Error(err.error || 'Ошибка сервера'); }); |
| | | 396 | | } |
| | | 397 | | return response.json(); |
| | | 398 | | }) |
| | | 399 | | .then(data => { |
| | | 400 | | if (data.success) { |
| | | 401 | | alert(translationText ? "Перевод сохранен" : "Перевод удален"); |
| | | 402 | | document.getElementById('translationInput').value = ''; |
| | | 403 | | location.reload(); |
| | | 404 | | } |
| | | 405 | | }) |
| | | 406 | | .catch(error => { |
| | | 407 | | console.error("Ошибка:", error); |
| | | 408 | | alert("Ошибка: " + error.message); |
| | | 409 | | }); |
| | | 410 | | } |
| | | 411 | | |
| | | 412 | | // Функция для отправки данных в контроллер |
| | | 413 | | function sendCategoriesToServer() { |
| | | 414 | | let selectedData = JSON.parse(sessionStorage.getItem("selectedCategories")) || {}; |
| | | 415 | | let endpoint = "" |
| | | 416 | | // Проверка наличия элемента с ключом 'CT' в selectedData |
| | | 417 | | if ("CT" in selectedData) { |
| | | 418 | | endpoint = 'CategoryTree/Category/LinkCategories' |
| | | 419 | | } else { |
| | | 420 | | endpoint = 'CategoryTree/Category/CreateUnifiedCategory' |
| | | 421 | | } |
| | | 422 | | |
| | | 423 | | fetch(endpoint, { |
| | | 424 | | method: 'POST', |
| | | 425 | | headers: { |
| | | 426 | | 'Content-Type': 'application/json' |
| | | 427 | | }, |
| | | 428 | | body: JSON.stringify(selectedData) |
| | | 429 | | }) |
| | | 430 | | .then(response => response.json()) |
| | | 431 | | .then(data => { |
| | | 432 | | console.log("Ответ сервера:", data); |
| | | 433 | | alert("Категории успешно связаны!"); |
| | | 434 | | sessionStorage.removeItem("selectedCategories"); // Очищаем после отправки |
| | | 435 | | location.reload(); |
| | | 436 | | }) |
| | | 437 | | .catch(error => { |
| | | 438 | | console.error("Ошибка:", error); |
| | | 439 | | alert("Ошибка при связывании категорий."); |
| | | 440 | | }); |
| | | 441 | | } |
| | | 442 | | </script> |