Добавил транспондеры к ObjItem шаблону

This commit is contained in:
2025-11-14 08:00:23 +03:00
parent 5ab6770809
commit 6a26991dc0
18 changed files with 3286 additions and 1188 deletions

View File

@@ -12,6 +12,16 @@
top: 0;
z-index: 10;
}
.btn-group .badge {
position: absolute;
top: -5px;
right: -5px;
font-size: 0.65rem;
padding: 0.2em 0.4em;
}
.btn-group .btn {
position: relative;
}
</style>
{% endblock %}
@@ -55,11 +65,20 @@
</select>
</div>
<!-- Action buttons -->
<div class="d-flex gap-2">
<button type="button" class="btn btn-outline-primary btn-sm" title="Показать на карте"
onclick="showSelectedOnMap()">
<i class="bi bi-map"></i> Карта
</button>
</div>
<!-- Filter Toggle Button -->
<div>
<button class="btn btn-outline-primary btn-sm" type="button" data-bs-toggle="offcanvas"
data-bs-target="#offcanvasFilters" aria-controls="offcanvasFilters">
<i class="bi bi-funnel"></i> Фильтры
<span id="filterCounter" class="badge bg-danger" style="display: none;">0</span>
</button>
</div>
@@ -185,6 +204,9 @@
<table class="table table-striped table-hover table-sm" style="font-size: 0.85rem;">
<thead class="table-dark sticky-top">
<tr>
<th scope="col" class="text-center" style="width: 3%;">
<input type="checkbox" id="select-all" class="form-check-input">
</th>
<th scope="col" class="text-center" style="min-width: 60px;">
<a href="javascript:void(0)" onclick="updateSort('id')" class="text-white text-decoration-none">
ID
@@ -229,12 +251,16 @@
{% endif %}
</a>
</th>
<th scope="col" class="text-center" style="min-width: 100px;">Детали</th>
<th scope="col" class="text-center" style="min-width: 150px;">Действия</th>
</tr>
</thead>
<tbody>
{% for source in processed_sources %}
<tr>
<td class="text-center">
<input type="checkbox" class="form-check-input item-checkbox"
value="{{ source.id }}">
</td>
<td class="text-center">{{ source.id }}</td>
<td>{{ source.coords_average }}</td>
<td>{{ source.coords_kupsat }}</td>
@@ -244,15 +270,44 @@
<td>{{ source.created_at|date:"d.m.Y H:i" }}</td>
<td>{{ source.updated_at|date:"d.m.Y H:i" }}</td>
<td class="text-center">
<button type="button" class="btn btn-sm btn-outline-primary"
onclick="showSourceDetails({{ source.id }})">
<i class="bi bi-eye"></i> Показать
</button>
<div class="btn-group" role="group">
{% if source.objitem_count > 0 %}
<a href="{% url 'mainapp:show_source_with_points_map' source.id %}"
target="_blank"
class="btn btn-sm btn-outline-success"
title="Показать источник с точками на карте">
<i class="bi bi-geo-alt"></i>
<span class="badge bg-success">{{ source.objitem_count }}</span>
</a>
{% else %}
<button type="button" class="btn btn-sm btn-outline-secondary" disabled title="Нет точек для отображения">
<i class="bi bi-geo-alt"></i>
</button>
{% endif %}
<button type="button" class="btn btn-sm btn-outline-primary"
onclick="showSourceDetails({{ source.id }})"
title="Показать детали">
<i class="bi bi-eye"></i>
</button>
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
<a href="{% url 'mainapp:source_update' source.id %}"
class="btn btn-sm btn-outline-warning"
title="Редактировать источник">
<i class="bi bi-pencil"></i>
</a>
{% else %}
<button type="button" class="btn btn-sm btn-outline-secondary" disabled title="Недостаточно прав">
<i class="bi bi-pencil"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="9" class="text-center text-muted">Нет данных для отображения</td>
<td colspan="10" class="text-center text-muted">Нет данных для отображения</td>
</tr>
{% endfor %}
</tbody>
@@ -285,6 +340,10 @@
<table class="table table-striped table-hover table-sm">
<thead class="table-light sticky-top">
<tr>
<th class="text-center" style="width: 3%;">
<input type="checkbox" id="modal-select-all" class="form-check-input">
</th>
<th class="text-center" style="min-width: 60px;">ID</th>
<th>Имя</th>
<th>Спутник</th>
<th>Частота, МГц</th>
@@ -319,6 +378,55 @@
{% block extra_js %}
<script>
let lastCheckedIndex = null;
function updateRowHighlight(checkbox) {
const row = checkbox.closest('tr');
if (checkbox.checked) {
row.classList.add('selected');
} else {
row.classList.remove('selected');
}
}
function handleCheckboxClick(e) {
if (e.shiftKey && lastCheckedIndex !== null) {
const checkboxes = document.querySelectorAll('.item-checkbox');
const currentIndex = Array.from(checkboxes).indexOf(e.target);
const startIndex = Math.min(lastCheckedIndex, currentIndex);
const endIndex = Math.max(lastCheckedIndex, currentIndex);
for (let i = startIndex; i <= endIndex; i++) {
checkboxes[i].checked = e.target.checked;
updateRowHighlight(checkboxes[i]);
}
} else {
updateRowHighlight(e.target);
}
lastCheckedIndex = Array.from(document.querySelectorAll('.item-checkbox')).indexOf(e.target);
}
// Function to show selected sources on map
function showSelectedOnMap() {
// Get all checked checkboxes
const checkedCheckboxes = document.querySelectorAll('.item-checkbox:checked');
if (checkedCheckboxes.length === 0) {
alert('Пожалуйста, выберите хотя бы один источник для отображения на карте');
return;
}
// Extract IDs from checked checkboxes
const selectedIds = [];
checkedCheckboxes.forEach(checkbox => {
selectedIds.push(checkbox.value);
});
// Redirect to the map view with selected IDs as query parameter
const url = '{% url "mainapp:show_sources_map" %}' + '?ids=' + selectedIds.join(',');
window.open(url, '_blank'); // Open in a new tab
}
// Search functionality
function performSearch() {
const searchValue = document.getElementById('toolbar-search').value.trim();
@@ -377,6 +485,103 @@ function updateSort(field) {
window.location.search = urlParams.toString();
}
// Setup radio-like behavior for filter checkboxes
function setupRadioLikeCheckboxes(name) {
const checkboxes = document.querySelectorAll(`input[name="${name}"]`);
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', function () {
if (this.checked) {
checkboxes.forEach(other => {
if (other !== this) {
other.checked = false;
}
});
}
});
});
}
// Filter counter functionality
function updateFilterCounter() {
const form = document.getElementById('filter-form');
const formData = new FormData(form);
let filterCount = 0;
// Count non-empty form fields
for (const [key, value] of formData.entries()) {
if (value && value.trim() !== '') {
filterCount++;
}
}
// Display the filter counter
const counterElement = document.getElementById('filterCounter');
if (counterElement) {
if (filterCount > 0) {
counterElement.textContent = filterCount;
counterElement.style.display = 'inline';
} else {
counterElement.style.display = 'none';
}
}
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
// Setup select-all checkbox
const selectAllCheckbox = document.getElementById('select-all');
const itemCheckboxes = document.querySelectorAll('.item-checkbox');
if (selectAllCheckbox && itemCheckboxes.length > 0) {
selectAllCheckbox.addEventListener('change', function () {
itemCheckboxes.forEach(checkbox => {
checkbox.checked = selectAllCheckbox.checked;
updateRowHighlight(checkbox);
});
});
itemCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', function () {
const allChecked = Array.from(itemCheckboxes).every(cb => cb.checked);
selectAllCheckbox.checked = allChecked;
});
// Add shift-click handler
checkbox.addEventListener('click', handleCheckboxClick);
});
}
// Setup radio-like checkboxes for filters
setupRadioLikeCheckboxes('has_coords_average');
setupRadioLikeCheckboxes('has_coords_kupsat');
setupRadioLikeCheckboxes('has_coords_valid');
setupRadioLikeCheckboxes('has_coords_reference');
// Update filter counter on page load
updateFilterCounter();
// Add event listeners to form elements to update counter when filters change
const form = document.getElementById('filter-form');
if (form) {
const inputFields = form.querySelectorAll('input[type="text"], input[type="number"], input[type="date"]');
inputFields.forEach(input => {
input.addEventListener('input', updateFilterCounter);
input.addEventListener('change', updateFilterCounter);
});
const checkboxFields = form.querySelectorAll('input[type="checkbox"]');
checkboxFields.forEach(checkbox => {
checkbox.addEventListener('change', updateFilterCounter);
});
}
// Update counter when offcanvas is shown
const offcanvasElement = document.getElementById('offcanvasFilters');
if (offcanvasElement) {
offcanvasElement.addEventListener('show.bs.offcanvas', updateFilterCounter);
}
});
// Show source details in modal
function showSourceDetails(sourceId) {
// Update modal title
@@ -420,6 +625,10 @@ function showSourceDetails(sourceId) {
data.objitems.forEach(objitem => {
const row = document.createElement('tr');
row.innerHTML = `
<td class="text-center">
<input type="checkbox" class="form-check-input modal-item-checkbox" value="${objitem.id}">
</td>
<td class="text-center">${objitem.id}</td>
<td>${objitem.name}</td>
<td>${objitem.satellite_name}</td>
<td>${objitem.frequency}</td>
@@ -434,6 +643,9 @@ function showSourceDetails(sourceId) {
`;
tbody.appendChild(row);
});
// Setup modal select-all checkbox
setupModalSelectAll();
} else {
// Show no data message
document.getElementById('modalNoData').style.display = 'block';
@@ -449,5 +661,32 @@ function showSourceDetails(sourceId) {
errorDiv.style.display = 'block';
});
}
// Setup select-all functionality for modal
function setupModalSelectAll() {
const modalSelectAll = document.getElementById('modal-select-all');
const modalItemCheckboxes = document.querySelectorAll('.modal-item-checkbox');
if (modalSelectAll && modalItemCheckboxes.length > 0) {
// Remove old event listeners by cloning
const newModalSelectAll = modalSelectAll.cloneNode(true);
modalSelectAll.parentNode.replaceChild(newModalSelectAll, modalSelectAll);
newModalSelectAll.addEventListener('change', function() {
const checkboxes = document.querySelectorAll('.modal-item-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = newModalSelectAll.checked;
});
});
modalItemCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
const allCheckboxes = document.querySelectorAll('.modal-item-checkbox');
const allChecked = Array.from(allCheckboxes).every(cb => cb.checked);
document.getElementById('modal-select-all').checked = allChecked;
});
});
}
}
</script>
{% endblock %}