Files
tourrism_site/public/js/admin-image-selector-fixed.js
Andrey K. Choi 13c752b93a feat: Оптимизация навигации AdminJS в логические группы
- Объединены ресурсы в 5 логических групп: Контент сайта, Бронирования, Отзывы и рейтинги, Персонал и гиды, Администрирование
- Удалены дублирующие настройки navigation для чистой группировки
- Добавлены CSS стили для визуального отображения иерархии с отступами
- Добавлены эмодзи-иконки для каждого типа ресурсов через CSS
- Улучшена навигация с правильной вложенностью элементов
2025-11-30 21:57:58 +09:00

309 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// JavaScript для интеграции редактора изображений в AdminJS
(function() {
'use strict';
// Функция для открытия редактора изображений
function openImageEditor(inputField, fieldName) {
const currentValue = inputField.value || '';
const editorUrl = `/image-editor-compact.html?field=${fieldName}&current=${encodeURIComponent(currentValue)}`;
// Убираем предыдущие модальные окна
document.querySelectorAll('.image-editor-modal').forEach(modal => modal.remove());
// Создаем модальное окно
const modal = document.createElement('div');
modal.className = 'image-editor-modal';
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
`;
const content = document.createElement('div');
content.style.cssText = `
background: white;
border-radius: 8px;
width: 90%;
max-width: 700px;
height: 80%;
max-height: 600px;
position: relative;
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
`;
const closeBtn = document.createElement('button');
closeBtn.innerHTML = '✕';
closeBtn.style.cssText = `
position: absolute;
top: 10px;
right: 10px;
background: #ff4757;
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
cursor: pointer;
z-index: 1;
font-size: 16px;
font-weight: bold;
`;
const iframe = document.createElement('iframe');
iframe.src = editorUrl;
iframe.style.cssText = `
width: 100%;
height: 100%;
border: none;
border-radius: 8px;
`;
// Обработчик сообщений от iframe
const messageHandler = function(event) {
if (event.data.type === 'imageSelected' && event.data.field === fieldName) {
console.log('🖼️ Изображение выбрано:', event.data.url);
inputField.value = event.data.url;
updateImagePreview(inputField, event.data.url);
// Триггерим событие change для обновления формы
const changeEvent = new Event('change', { bubbles: true });
inputField.dispatchEvent(changeEvent);
// Триггерим input событие
const inputEvent = new Event('input', { bubbles: true });
inputField.dispatchEvent(inputEvent);
modal.remove();
window.removeEventListener('message', messageHandler);
} else if (event.data.type === 'editorClosed') {
modal.remove();
window.removeEventListener('message', messageHandler);
}
};
window.addEventListener('message', messageHandler);
closeBtn.onclick = function() {
modal.remove();
window.removeEventListener('message', messageHandler);
};
modal.onclick = function(e) {
if (e.target === modal) {
modal.remove();
window.removeEventListener('message', messageHandler);
}
};
content.appendChild(closeBtn);
content.appendChild(iframe);
modal.appendChild(content);
document.body.appendChild(modal);
}
// Функция обновления превью изображения
function updateImagePreview(inputField, imagePath) {
const fieldContainer = inputField.closest('.field, .property-edit, div[data-testid]') || inputField.parentNode;
if (!fieldContainer) return;
// Находим или создаем превью
let preview = fieldContainer.querySelector('.image-preview');
if (!preview) {
preview = document.createElement('img');
preview.className = 'image-preview';
preview.style.cssText = `
display: block;
max-width: 180px;
max-height: 120px;
object-fit: cover;
border: 1px solid #ddd;
border-radius: 4px;
margin-top: 8px;
margin-bottom: 8px;
`;
// Вставляем превью после кнопки
const button = fieldContainer.querySelector('.image-editor-btn');
if (button) {
const buttonContainer = button.parentNode;
buttonContainer.parentNode.insertBefore(preview, buttonContainer.nextSibling);
} else {
fieldContainer.appendChild(preview);
}
}
if (imagePath && imagePath.trim()) {
preview.src = imagePath + '?t=' + Date.now(); // Добавляем timestamp для обновления
preview.style.display = 'block';
preview.onerror = () => {
preview.style.display = 'none';
};
} else {
preview.style.display = 'none';
}
}
// Функция добавления кнопки редактора к полю
function addImageEditorButton(inputField) {
const fieldName = inputField.name || inputField.id || 'image';
// Проверяем, не добавлена ли уже кнопка
const fieldContainer = inputField.closest('.field, .property-edit, div[data-testid]') || inputField.parentNode;
if (fieldContainer.querySelector('.image-editor-btn')) {
return;
}
// Создаем контейнер для кнопки
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = `
margin-top: 8px;
display: flex;
align-items: flex-start;
gap: 10px;
`;
// Создаем кнопку
const button = document.createElement('button');
button.type = 'button';
button.className = 'image-editor-btn';
button.innerHTML = '📷 Выбрать';
button.style.cssText = `
padding: 6px 12px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
white-space: nowrap;
`;
button.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
openImageEditor(inputField, fieldName);
};
buttonContainer.appendChild(button);
// Добавляем контейнер после поля ввода
if (inputField.nextSibling) {
inputField.parentNode.insertBefore(buttonContainer, inputField.nextSibling);
} else {
inputField.parentNode.appendChild(buttonContainer);
}
// Добавляем превью если есть значение
if (inputField.value && inputField.value.trim()) {
updateImagePreview(inputField, inputField.value);
}
// Слушаем изменения в поле для обновления превью
inputField.addEventListener('input', () => {
updateImagePreview(inputField, inputField.value);
});
inputField.addEventListener('change', () => {
updateImagePreview(inputField, inputField.value);
});
}
// Функция проверки, является ли поле полем изображения
function isImageField(inputField) {
const fieldName = (inputField.name || inputField.id || '').toLowerCase();
let labelText = '';
// Ищем label для поля
const fieldContainer = inputField.closest('.field, .property-edit, div[data-testid]');
if (fieldContainer) {
const label = fieldContainer.querySelector('label, .property-label, h3');
if (label) {
labelText = label.textContent.toLowerCase();
}
}
// Проверяем по имени поля и содержанию
const isImageByName = fieldName.includes('image') && !fieldName.includes('title') && !fieldName.includes('alt');
const isImageByLabel = labelText.includes('image') || labelText.includes('изображение') || labelText.includes('фото');
const isImageUrlField = fieldName.includes('image_url') || fieldName === 'image_url';
console.log(`🔍 Проверка поля "${fieldName}": isImageByName=${isImageByName}, isImageByLabel=${isImageByLabel}, isImageUrlField=${isImageUrlField}`);
return isImageUrlField || isImageByName || isImageByLabel;
}
// Функция сканирования и добавления кнопок к полям изображений
function scanAndAddImageButtons() {
console.log('🔍 Сканирование полей для добавления кнопок редактора изображений...');
// Более широкий поиск полей ввода
const inputFields = document.querySelectorAll('input[type="text"], input[type="url"], input:not([type="hidden"]):not([type="submit"]):not([type="button"])');
console.log(`📋 Найдено ${inputFields.length} полей ввода`);
inputFields.forEach((inputField, index) => {
const fieldName = inputField.name || inputField.id || `field_${index}`;
const isImage = isImageField(inputField);
const fieldContainer = inputField.closest('.field, .property-edit, div[data-testid]') || inputField.parentNode;
const hasButton = fieldContainer.querySelector('.image-editor-btn');
console.log(`🔸 Поле "${fieldName}": isImage=${isImage}, hasButton=${!!hasButton}`);
if (isImage && !hasButton) {
console.log(` Добавляем кнопку для поля "${fieldName}"`);
addImageEditorButton(inputField);
}
});
}
// Инициализация при загрузке DOM
function initialize() {
console.log('🚀 Инициализация селектора изображений AdminJS');
scanAndAddImageButtons();
// Наблюдаем за изменениями в DOM для динамически добавляемых полей
const observer = new MutationObserver((mutations) => {
let shouldScan = false;
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && (node.tagName === 'INPUT' || node.querySelector('input'))) {
shouldScan = true;
}
});
}
});
if (shouldScan) {
setTimeout(scanAndAddImageButtons, 100);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// Ждем загрузки DOM
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
// Также запускаем через задержки для AdminJS
setTimeout(initialize, 1000);
setTimeout(initialize, 3000);
setTimeout(initialize, 5000);
})();