This commit is contained in:
+82
@@ -1092,6 +1092,88 @@ def api_load_categories():
|
|||||||
log_event(error_msg)
|
log_event(error_msg)
|
||||||
return jsonify({'success': False, 'error': error_msg}), 500
|
return jsonify({'success': False, 'error': error_msg}), 500
|
||||||
|
|
||||||
|
@bp.route('/api/categories')
|
||||||
|
@login_required
|
||||||
|
def api_categories():
|
||||||
|
"""Получает список всех категорий (площадок) из таблицы categories"""
|
||||||
|
db = get_db()
|
||||||
|
try:
|
||||||
|
with db.conn.cursor() as cursor:
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT AUTO_ID, ID, TITLE, description
|
||||||
|
FROM categories
|
||||||
|
ORDER BY TITLE, ID
|
||||||
|
""")
|
||||||
|
categories = cursor.fetchall()
|
||||||
|
|
||||||
|
categories_list = []
|
||||||
|
for cat in categories:
|
||||||
|
categories_list.append({
|
||||||
|
'auto_id': cat['AUTO_ID'],
|
||||||
|
'id': cat['ID'],
|
||||||
|
'title': cat['TITLE'] or '',
|
||||||
|
'description': cat['description'] or ''
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify(categories_list)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
@bp.route('/api/categories/<category_id>')
|
||||||
|
@login_required
|
||||||
|
def api_category_details(category_id):
|
||||||
|
"""Получает детали категории по ID"""
|
||||||
|
db = get_db()
|
||||||
|
try:
|
||||||
|
with db.conn.cursor() as cursor:
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT AUTO_ID, ID, TITLE, description
|
||||||
|
FROM categories
|
||||||
|
WHERE ID = %s
|
||||||
|
""", (category_id,))
|
||||||
|
category = cursor.fetchone()
|
||||||
|
|
||||||
|
if not category:
|
||||||
|
return jsonify({'error': 'Category not found'}), 404
|
||||||
|
|
||||||
|
# Получаем количество событий для этой категории
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT COUNT(*) as event_count
|
||||||
|
FROM events
|
||||||
|
WHERE unit_name = %s
|
||||||
|
""", (category_id,))
|
||||||
|
event_count_result = cursor.fetchone()
|
||||||
|
event_count = event_count_result['event_count'] if event_count_result else 0
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'auto_id': category['AUTO_ID'],
|
||||||
|
'id': category['ID'],
|
||||||
|
'title': category['TITLE'] or '',
|
||||||
|
'description': category['description'] or '',
|
||||||
|
'event_count': event_count
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
@bp.route('/api/events/count')
|
||||||
|
@login_required
|
||||||
|
def api_events_count():
|
||||||
|
"""Получает количество событий в таблице events"""
|
||||||
|
db = get_db()
|
||||||
|
try:
|
||||||
|
with db.conn.cursor() as cursor:
|
||||||
|
cursor.execute("SELECT COUNT(*) as count FROM events")
|
||||||
|
result = cursor.fetchone()
|
||||||
|
return jsonify({'count': result['count'] if result else 0})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
@bp.route('/api/run_volk_rescan', methods=['POST'])
|
@bp.route('/api/run_volk_rescan', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def api_run_volk_rescan():
|
def api_run_volk_rescan():
|
||||||
|
|||||||
+402
-32
@@ -451,6 +451,13 @@
|
|||||||
<i class="fas fa-calendar-alt me-1"></i> События
|
<i class="fas fa-calendar-alt me-1"></i> События
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
{% if g.workmode == 'VOLK' %}
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="categories-tab" data-bs-toggle="tab" data-bs-target="#categories-tab-pane" type="button" role="tab" aria-controls="categories-tab-pane" aria-selected="false">
|
||||||
|
<i class="fas fa-map-marker-alt me-1"></i> Площадки
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
<!-- Содержимое вкладок -->
|
<!-- Содержимое вкладок -->
|
||||||
<div class="tab-content" id="mainTabsContent">
|
<div class="tab-content" id="mainTabsContent">
|
||||||
@@ -573,11 +580,6 @@
|
|||||||
<button class="btn btn-success" id="publish-all-events-btn">
|
<button class="btn btn-success" id="publish-all-events-btn">
|
||||||
<i class="fas fa-paper-plane me-1"></i> Опубликовать все
|
<i class="fas fa-paper-plane me-1"></i> Опубликовать все
|
||||||
</button>
|
</button>
|
||||||
{% if g.workmode == 'VOLK' %}
|
|
||||||
<button class="btn btn-info ms-2" id="load-categories-btn">
|
|
||||||
<i class="fas fa-sync-alt me-1"></i> Площадки
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
<div id="event-selected-post-info" class="cross-tab-info" style="display: none;">
|
<div id="event-selected-post-info" class="cross-tab-info" style="display: none;">
|
||||||
<span>Выбранный пост: </span>
|
<span>Выбранный пост: </span>
|
||||||
<span id="event-selected-post-name"></span>
|
<span id="event-selected-post-name"></span>
|
||||||
@@ -661,6 +663,83 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if g.workmode == 'VOLK' %}
|
||||||
|
<!-- Вкладка площадок -->
|
||||||
|
<div class="tab-pane fade" id="categories-tab-pane" role="tabpanel" aria-labelledby="categories-tab" tabindex="0">
|
||||||
|
<div class="top-action-buttons d-flex align-items-center">
|
||||||
|
<button class="btn btn-info" id="load-categories-btn">
|
||||||
|
<i class="fas fa-sync-alt me-1"></i> Площадки
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<!-- Левая панель - список площадок -->
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0">Список площадок</h5>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="input-group me-2" style="width: 200px;">
|
||||||
|
<input type="text" class="form-control form-control-sm" id="categories-search" placeholder="Поиск...">
|
||||||
|
<button class="btn btn-outline-secondary btn-sm" type="button" id="categories-search-prev">
|
||||||
|
<i class="fas fa-chevron-up"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary btn-sm" type="button" id="categories-search-next">
|
||||||
|
<i class="fas fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-primary" id="refresh-categories-btn">
|
||||||
|
<i class="fas fa-sync-alt"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<!-- Контейнер для фиксированного заголовка -->
|
||||||
|
<div class="table-header-container">
|
||||||
|
<table class="table table-hover mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th class="sortable-header">ID</th>
|
||||||
|
<th class="sortable-header">Название</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- Контейнер для скроллируемого тела таблицы -->
|
||||||
|
<div class="scrollable-table-body">
|
||||||
|
<table class="table table-hover mb-0">
|
||||||
|
<tbody id="categories-table">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-center py-5">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">Загрузка...</span>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2">Загрузка данных...</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Правая панель - детали площадки -->
|
||||||
|
<div class="col-md-7">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0" id="category-details-header">Детали площадки</h5>
|
||||||
|
<div id="category-actions"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body scrollable-details" id="category-details">
|
||||||
|
<div class="text-center text-muted py-5">
|
||||||
|
<i class="fas fa-info-circle fa-3x mb-3"></i>
|
||||||
|
<p>Выберите площадку для просмотра деталей</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Модальное окно подтверждения удаления поста -->
|
<!-- Модальное окно подтверждения удаления поста -->
|
||||||
@@ -866,6 +945,21 @@
|
|||||||
syncColumnWidths('events');
|
syncColumnWidths('events');
|
||||||
}, 50);
|
}, 50);
|
||||||
});
|
});
|
||||||
|
{% if g.workmode == 'VOLK' %}
|
||||||
|
const categoriesTab = document.getElementById('categories-tab');
|
||||||
|
if (categoriesTab) {
|
||||||
|
categoriesTab.addEventListener('click', () => {
|
||||||
|
if (document.getElementById('categories-table').children.length === 1) {
|
||||||
|
loadCategoriesList();
|
||||||
|
}
|
||||||
|
// Принудительно обновляем размеры панелей и синхронизируем колонки
|
||||||
|
setTimeout(() => {
|
||||||
|
adjustPanelWidthsOnResize();
|
||||||
|
syncColumnWidths('categories');
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
// Новые обработчики для кнопок событий
|
// Новые обработчики для кнопок событий
|
||||||
{% if g.workmode == 'VOLK' %}
|
{% if g.workmode == 'VOLK' %}
|
||||||
const volkRescanBtn = document.getElementById('volk-rescan-btn');
|
const volkRescanBtn = document.getElementById('volk-rescan-btn');
|
||||||
@@ -885,6 +979,10 @@
|
|||||||
if (loadCategoriesBtn) {
|
if (loadCategoriesBtn) {
|
||||||
loadCategoriesBtn.addEventListener('click', loadCategories);
|
loadCategoriesBtn.addEventListener('click', loadCategories);
|
||||||
}
|
}
|
||||||
|
const refreshCategoriesBtn = document.getElementById('refresh-categories-btn');
|
||||||
|
if (refreshCategoriesBtn) {
|
||||||
|
refreshCategoriesBtn.addEventListener('click', loadCategoriesList);
|
||||||
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
// Обработчики для кнопок привязки
|
// Обработчики для кнопок привязки
|
||||||
document.getElementById('post-link-event-btn').addEventListener('click', togglePostEventLink);
|
document.getElementById('post-link-event-btn').addEventListener('click', togglePostEventLink);
|
||||||
@@ -1034,28 +1132,76 @@
|
|||||||
}
|
}
|
||||||
// Функция для загрузки категорий
|
// Функция для загрузки категорий
|
||||||
function loadCategories() {
|
function loadCategories() {
|
||||||
showProcessingMessage("Загрузка категорий...");
|
// Сначала проверяем наличие событий
|
||||||
fetch(`${PREFIX}/api/load_categories`, {
|
fetch(`${PREFIX}/api/events/count`)
|
||||||
method: 'POST',
|
.then(response => response.json())
|
||||||
headers: {
|
.then(data => {
|
||||||
'Content-Type': 'application/json',
|
const eventCount = data.count || 0;
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
if (eventCount > 0) {
|
||||||
}
|
// Если есть события, спрашиваем подтверждение
|
||||||
})
|
if (!confirm(`В таблице событий есть ${eventCount} записей. Обновление данных площадок может повлиять на существующие события. Вы уверены, что хотите продолжить?`)) {
|
||||||
.then(response => response.json())
|
return;
|
||||||
.then(data => {
|
}
|
||||||
hideProcessingMessage();
|
}
|
||||||
if (data.success) {
|
// Продолжаем загрузку категорий
|
||||||
alert('Категории загружены успешно!');
|
showProcessingMessage("Загрузка категорий...");
|
||||||
} else {
|
fetch(`${PREFIX}/api/load_categories`, {
|
||||||
alert('Ошибка загрузки категорий: ' + (data.error || 'неизвестная ошибка'));
|
method: 'POST',
|
||||||
}
|
headers: {
|
||||||
})
|
'Content-Type': 'application/json',
|
||||||
.catch(error => {
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
hideProcessingMessage();
|
}
|
||||||
console.error('Ошибка загрузки категорий:', error);
|
})
|
||||||
alert('Произошла ошибка при загрузке категорий');
|
.then(response => response.json())
|
||||||
});
|
.then(data => {
|
||||||
|
hideProcessingMessage();
|
||||||
|
if (data.success) {
|
||||||
|
alert('Категории загружены успешно!');
|
||||||
|
// Обновляем список категорий, если мы на вкладке площадок
|
||||||
|
const categoriesTab = document.getElementById('categories-tab');
|
||||||
|
if (categoriesTab && categoriesTab.classList.contains('active')) {
|
||||||
|
loadCategoriesList();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert('Ошибка загрузки категорий: ' + (data.error || 'неизвестная ошибка'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
hideProcessingMessage();
|
||||||
|
console.error('Ошибка загрузки категорий:', error);
|
||||||
|
alert('Произошла ошибка при загрузке категорий');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Ошибка проверки количества событий:', error);
|
||||||
|
// В случае ошибки все равно продолжаем загрузку
|
||||||
|
showProcessingMessage("Загрузка категорий...");
|
||||||
|
fetch(`${PREFIX}/api/load_categories`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
hideProcessingMessage();
|
||||||
|
if (data.success) {
|
||||||
|
alert('Категории загружены успешно!');
|
||||||
|
const categoriesTab = document.getElementById('categories-tab');
|
||||||
|
if (categoriesTab && categoriesTab.classList.contains('active')) {
|
||||||
|
loadCategoriesList();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert('Ошибка загрузки категорий: ' + (data.error || 'неизвестная ошибка'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
hideProcessingMessage();
|
||||||
|
console.error('Ошибка загрузки категорий:', err);
|
||||||
|
alert('Произошла ошибка при загрузке категорий');
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Функция для публикации конкретного события
|
// Функция для публикации конкретного события
|
||||||
function publishCurrentEvent(eventId) {
|
function publishCurrentEvent(eventId) {
|
||||||
@@ -1680,6 +1826,142 @@
|
|||||||
console.error('Ошибка загрузки деталей события:', error);
|
console.error('Ошибка загрузки деталей события:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Функции для работы с категориями (площадками)
|
||||||
|
let selectedCategoryId = null;
|
||||||
|
async function loadCategoriesList() {
|
||||||
|
const tableBody = document.getElementById('categories-table');
|
||||||
|
if (!tableBody) return;
|
||||||
|
tableBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-center py-3">
|
||||||
|
<div class="spinner-border spinner-border-sm" role="status">
|
||||||
|
<span class="visually-hidden">Загрузка...</span>
|
||||||
|
</div>
|
||||||
|
Загрузка площадок...
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${PREFIX}/api/categories`);
|
||||||
|
const categories = await response.json();
|
||||||
|
if (categories.length === 0) {
|
||||||
|
tableBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-center py-5 text-muted">
|
||||||
|
<i class="fas fa-inbox fa-3x mb-3"></i>
|
||||||
|
<p>Нет доступных площадок</p>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tableBody.innerHTML = '';
|
||||||
|
categories.forEach(category => {
|
||||||
|
const row = `
|
||||||
|
<tr class="category-row" data-id="${category.id}">
|
||||||
|
<td>${category.id || '-'}</td>
|
||||||
|
<td class="category-name-preview" title="${category.title || ''}">${category.title || 'Без названия'}</td>
|
||||||
|
</tr>`;
|
||||||
|
tableBody.innerHTML += row;
|
||||||
|
});
|
||||||
|
// Клик по строке категории
|
||||||
|
document.querySelectorAll('.category-row').forEach(row => {
|
||||||
|
row.addEventListener('click', (e) => {
|
||||||
|
const categoryId = row.dataset.id;
|
||||||
|
showCategoryDetails(categoryId);
|
||||||
|
highlightSelectedCategory(categoryId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Переинициализируем разделители колонок для категорий
|
||||||
|
setTimeout(() => {
|
||||||
|
syncColumnWidths('categories');
|
||||||
|
reinitializeColumnResizers('categories');
|
||||||
|
}, 0);
|
||||||
|
} catch (error) {
|
||||||
|
tableBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-center py-5 text-danger">
|
||||||
|
<i class="fas fa-exclamation-triangle fa-2x mb-3"></i>
|
||||||
|
<p>Ошибка загрузки данных</p>
|
||||||
|
<button class="btn btn-sm btn-outline-primary mt-2" onclick="loadCategoriesList()">
|
||||||
|
Повторить попытку
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
console.error('Ошибка загрузки категорий:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Выделение выбранной категории
|
||||||
|
function highlightSelectedCategory(categoryId) {
|
||||||
|
// Снимаем выделение со всех категорий
|
||||||
|
document.querySelectorAll('.category-row').forEach(row => {
|
||||||
|
row.classList.remove('active');
|
||||||
|
});
|
||||||
|
// Находим нужную категорию
|
||||||
|
const selectedRow = document.querySelector(`.category-row[data-id="${categoryId}"]`);
|
||||||
|
if (selectedRow) {
|
||||||
|
// Выделяем категорию
|
||||||
|
selectedRow.classList.add('active');
|
||||||
|
selectedCategoryId = categoryId;
|
||||||
|
// Прокручиваем к выбранной категории
|
||||||
|
selectedRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Показать детали категории
|
||||||
|
async function showCategoryDetails(categoryId) {
|
||||||
|
const detailsPanel = document.getElementById('category-details');
|
||||||
|
if (!detailsPanel) return;
|
||||||
|
detailsPanel.innerHTML = `
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">Загрузка...</span>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2">Загрузка данных площадки...</p>
|
||||||
|
</div>`;
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${PREFIX}/api/categories/${encodeURIComponent(categoryId)}`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Площадка не найдена');
|
||||||
|
}
|
||||||
|
const category = await response.json();
|
||||||
|
// Создаем HTML для деталей
|
||||||
|
let detailsHtml = `
|
||||||
|
<div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="mb-2">
|
||||||
|
<strong>ID:</strong> ${category.id || 'Не указано'}
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<strong>Название:</strong> ${category.title || 'Не указано'}
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<strong>Количество событий:</strong> ${category.event_count || 0}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
if (category.description) {
|
||||||
|
detailsHtml += `
|
||||||
|
<div class="mb-4 mt-3">
|
||||||
|
<strong>Описание:</strong>
|
||||||
|
<div class="border p-3 bg-light rounded mt-2">${category.description}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
detailsHtml += `</div>`;
|
||||||
|
detailsPanel.innerHTML = detailsHtml;
|
||||||
|
// Обновляем заголовок панели
|
||||||
|
const header = document.getElementById('category-details-header');
|
||||||
|
if (header) {
|
||||||
|
header.textContent = `Площадка: ${category.title || category.id || 'Без названия'}`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
detailsPanel.innerHTML = `
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||||
|
Ошибка загрузки данных площадки: ${error.message}
|
||||||
|
<button class="btn btn-sm btn-outline-primary mt-2" onclick="showCategoryDetails('${categoryId}')">
|
||||||
|
Повторить попытку
|
||||||
|
</button>
|
||||||
|
</div>`;
|
||||||
|
console.error('Ошибка загрузки деталей категории:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Функция загрузки постов
|
// Функция загрузки постов
|
||||||
async function loadPosts() {
|
async function loadPosts() {
|
||||||
const tableBody = document.getElementById('posts-table');
|
const tableBody = document.getElementById('posts-table');
|
||||||
@@ -2550,11 +2832,17 @@ function setupColumnResizer(resizer, header, tableType = 'posts') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Применяем ширину к соответствующим ячейкам в теле таблицы
|
// Применяем ширину к соответствующим ячейкам в теле таблицы
|
||||||
const bodyTable = document.querySelector(
|
let bodyTableSelector;
|
||||||
tableType === 'posts'
|
if (tableType === 'posts') {
|
||||||
? '#posts-tab-pane .scrollable-table-body table'
|
bodyTableSelector = '#posts-tab-pane .scrollable-table-body table';
|
||||||
: '#events-tab-pane .scrollable-table-body table'
|
} else if (tableType === 'events') {
|
||||||
);
|
bodyTableSelector = '#events-tab-pane .scrollable-table-body table';
|
||||||
|
} else if (tableType === 'categories') {
|
||||||
|
bodyTableSelector = '#categories-tab-pane .scrollable-table-body table';
|
||||||
|
} else {
|
||||||
|
bodyTableSelector = '#posts-tab-pane .scrollable-table-body table';
|
||||||
|
}
|
||||||
|
const bodyTable = document.querySelector(bodyTableSelector);
|
||||||
|
|
||||||
if (bodyTable) {
|
if (bodyTable) {
|
||||||
const bodyRows = bodyTable.querySelectorAll('tbody tr');
|
const bodyRows = bodyTable.querySelectorAll('tbody tr');
|
||||||
@@ -2595,6 +2883,9 @@ function syncColumnWidths(tableType) {
|
|||||||
} else if (tableType === 'events') {
|
} else if (tableType === 'events') {
|
||||||
headerContainerSelector = '#events-tab-pane .table-header-container table';
|
headerContainerSelector = '#events-tab-pane .table-header-container table';
|
||||||
bodyContainerSelector = '#events-tab-pane .scrollable-table-body table';
|
bodyContainerSelector = '#events-tab-pane .scrollable-table-body table';
|
||||||
|
} else if (tableType === 'categories') {
|
||||||
|
headerContainerSelector = '#categories-tab-pane .table-header-container table';
|
||||||
|
bodyContainerSelector = '#categories-tab-pane .scrollable-table-body table';
|
||||||
} else {
|
} else {
|
||||||
console.warn('Неизвестный тип таблицы для синхронизации:', tableType);
|
console.warn('Неизвестный тип таблицы для синхронизации:', tableType);
|
||||||
return;
|
return;
|
||||||
@@ -3088,6 +3379,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
syncColumnWidths('posts');
|
syncColumnWidths('posts');
|
||||||
} else if (tabId === 'events-tab-pane') {
|
} else if (tabId === 'events-tab-pane') {
|
||||||
syncColumnWidths('events');
|
syncColumnWidths('events');
|
||||||
|
} else if (tabId === 'categories-tab-pane') {
|
||||||
|
syncColumnWidths('categories');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
@@ -3541,6 +3834,56 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Простой поиск по событиям (без тэгов)
|
// Простой поиск по событиям (без тэгов)
|
||||||
|
{% if g.workmode == 'VOLK' %}
|
||||||
|
// Функция поиска по категориям
|
||||||
|
function searchCategories(query) {
|
||||||
|
const tableBody = document.getElementById('categories-table');
|
||||||
|
if (!tableBody) return;
|
||||||
|
const rows = tableBody.querySelectorAll('.category-row');
|
||||||
|
const searchTerm = query.toLowerCase().trim();
|
||||||
|
let foundCount = 0;
|
||||||
|
let firstMatch = null;
|
||||||
|
rows.forEach((row, index) => {
|
||||||
|
const id = row.querySelector('td:first-child').textContent.toLowerCase();
|
||||||
|
const name = row.querySelector('.category-name-preview').textContent.toLowerCase();
|
||||||
|
const matches = id.includes(searchTerm) || name.includes(searchTerm);
|
||||||
|
if (matches) {
|
||||||
|
row.style.display = '';
|
||||||
|
if (foundCount === 0) {
|
||||||
|
firstMatch = row;
|
||||||
|
}
|
||||||
|
foundCount++;
|
||||||
|
} else {
|
||||||
|
row.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Выделяем первый найденный результат
|
||||||
|
if (firstMatch && searchTerm) {
|
||||||
|
const categoryId = firstMatch.dataset.id;
|
||||||
|
highlightSelectedCategory(categoryId);
|
||||||
|
showCategoryDetails(categoryId);
|
||||||
|
firstMatch.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Навигация по результатам поиска категорий
|
||||||
|
let currentCategorySearchIndex = -1;
|
||||||
|
function navigateCategoriesSearch(direction) {
|
||||||
|
const tableBody = document.getElementById('categories-table');
|
||||||
|
if (!tableBody) return;
|
||||||
|
const visibleRows = Array.from(tableBody.querySelectorAll('.category-row')).filter(row => row.style.display !== 'none');
|
||||||
|
if (visibleRows.length === 0) return;
|
||||||
|
if (direction === 'next') {
|
||||||
|
currentCategorySearchIndex = (currentCategorySearchIndex + 1) % visibleRows.length;
|
||||||
|
} else {
|
||||||
|
currentCategorySearchIndex = (currentCategorySearchIndex - 1 + visibleRows.length) % visibleRows.length;
|
||||||
|
}
|
||||||
|
const selectedRow = visibleRows[currentCategorySearchIndex];
|
||||||
|
const categoryId = selectedRow.dataset.id;
|
||||||
|
highlightSelectedCategory(categoryId);
|
||||||
|
showCategoryDetails(categoryId);
|
||||||
|
selectedRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
function searchEventsSimple(query) {
|
function searchEventsSimple(query) {
|
||||||
const events = document.querySelectorAll('.event-row');
|
const events = document.querySelectorAll('.event-row');
|
||||||
eventsSearchResults = [];
|
eventsSearchResults = [];
|
||||||
@@ -3673,6 +4016,33 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{% if g.workmode == 'VOLK' %}
|
||||||
|
const categoriesSearchInput = document.getElementById('categories-search');
|
||||||
|
if (categoriesSearchInput) {
|
||||||
|
categoriesSearchInput.addEventListener('input', (e) => {
|
||||||
|
searchCategories(e.target.value);
|
||||||
|
currentCategorySearchIndex = -1;
|
||||||
|
});
|
||||||
|
document.getElementById('categories-search-prev').addEventListener('click', () => {
|
||||||
|
navigateCategoriesSearch('prev');
|
||||||
|
});
|
||||||
|
document.getElementById('categories-search-next').addEventListener('click', () => {
|
||||||
|
navigateCategoriesSearch('next');
|
||||||
|
});
|
||||||
|
categoriesSearchInput.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
navigateCategoriesSearch('next');
|
||||||
|
} else if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
navigateCategoriesSearch('prev');
|
||||||
|
} else if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
navigateCategoriesSearch('next');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
document.getElementById('events-search').addEventListener('keydown', (e) => {
|
document.getElementById('events-search').addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
Reference in New Issue
Block a user