Files
dbstorage/dbapp/mainapp/templates/mainapp/objitem_form.html

632 lines
25 KiB
HTML
Raw 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.

{% extends 'mainapp/base.html' %}
{% load static %}
{% load static leaflet_tags %}
{% load l10n %}
{% block title %}{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать объект{% endif %}{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/checkbox-select-multiple.css' %}">
<style>
.form-section {
margin-bottom: 2rem;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
padding: 1rem;
}
.form-section-header {
border-bottom: 1px solid #dee2e6;
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
.btn-action {
margin-right: 0.5rem;
}
.dynamic-form {
border: 1px dashed #ced4da;
padding: 1rem;
margin-top: 1rem;
border-radius: 0.25rem;
}
.dynamic-form-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.readonly-field {
background-color: #f8f9fa;
padding: 0.375rem 0.75rem;
border: 1px solid #ced4da;
border-radius: 0.25rem;
}
.coord-group {
border: 1px solid #dee2e6;
padding: 0.75rem;
border-radius: 0.25rem;
margin-bottom: 1rem;
}
.coord-group-header {
font-weight: bold;
margin-bottom: 0.5rem;
}
.form-check-input {
margin-top: 0.25rem;
}
.datetime-group {
display: flex;
gap: 1rem;
}
.datetime-group>div {
flex: 1;
}
#map {
height: 500px;
width: 100%;
margin-bottom: 1rem;
}
.map-container {
margin-bottom: 1rem;
}
.coord-sync-group {
border: 1px solid #dee2e6;
padding: 0.75rem;
border-radius: 0.25rem;
}
.map-controls {
display: flex;
gap: 10px;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.map-control-btn {
padding: 0.375rem 0.75rem;
border: 1px solid #ced4da;
background-color: #f8f9fa;
border-radius: 0.25rem;
cursor: pointer;
}
.map-control-btn.active {
background-color: #e9ecef;
border-color: #dee2e6;
}
.map-control-btn.edit {
background-color: #fff3cd;
border-color: #ffeeba;
}
.map-control-btn.save {
background-color: #d1ecf1;
border-color: #bee5eb;
}
.map-control-btn.cancel {
background-color: #f8d7da;
border-color: #f5c6cb;
}
.leaflet-marker-icon {
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.3));
}
/* Select2 custom styling */
.select2-container--default .select2-selection--multiple {
border: 1px solid #ced4da;
border-radius: 0.25rem;
min-height: 38px;
}
.select2-container--default.select2-container--focus .select2-selection--multiple {
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid px-3">
<div class="row mb-3">
<div class="col-12 d-flex justify-content-between align-items-center">
<h2>{% if object %}Редактировать объект: {{ object.name }}{% else %}Создать объект{% endif %}</h2>
<div>
{% if user.customuser.role == 'admin' or user.customuser.role == 'moderator' %}
<button type="submit" form="objitem-form" class="btn btn-primary btn-action">Сохранить</button>
{% if object %}
<a href="{% url 'mainapp:objitem_delete' object.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}"
class="btn btn-danger btn-action">Удалить</a>
{% endif %}
{% endif %}
<a href="{% url 'mainapp:objitem_list' %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}"
class="btn btn-secondary btn-action">Назад</a>
</div>
</div>
</div>
<form method="post" id="objitem-form">
{% csrf_token %}
<!-- Основная информация -->
<div class="form-section">
<div class="form-section-header">
<h4>Основная информация</h4>
</div>
<div class="row">
<div class="col-md-6">
{% include 'mainapp/components/_form_field.html' with field=form.name %}
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Дата создания:</label>
<div class="readonly-field">
{% if object.created_at %}{{ object.created_at|date:"d.m.Y H:i" }}{% else %}-{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Создан пользователем:</label>
<div class="readonly-field">
{% if object.created_by %}{{ object.created_by }}{% else %}-{% endif %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Дата последнего изменения:</label>
<div class="readonly-field">
{% if object.updated_at %}{{ object.updated_at|date:"d.m.Y H:i" }}{% else %}-{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Изменен пользователем:</label>
<div class="readonly-field">
{% if object.updated_by %}{{ object.updated_by }}{% else %}-{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- ВЧ загрузка -->
<div class="form-section">
<div class="form-section-header">
<h4>ВЧ загрузка</h4>
</div>
<div id="parameters-container">
<div class="row">
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.id_satellite %}
</div>
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.frequency %}
</div>
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.freq_range %}
</div>
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.polarization %}
</div>
</div>
<div class="row">
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.bod_velocity %}
</div>
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.modulation %}
</div>
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.snr %}
</div>
<div class="col-md-3">
{% include 'mainapp/components/_form_field.html' with field=parameter_form.standard %}
</div>
</div>
</div>
</div>
<!-- Транспондер -->
<div class="form-section">
<div class="form-section-header">
<h4>Транспондер</h4>
</div>
{% if object.transponder %}
<div class="row">
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">Название:</label>
<div class="readonly-field">{{ object.transponder.name|default:"-" }}</div>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">Спутник:</label>
<div class="readonly-field">{{ object.transponder.sat_id.name|default:"-" }}</div>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">Downlink (МГц):</label>
<div class="readonly-field">{{ object.transponder.downlink|default:"-" }}</div>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">Uplink (МГц):</label>
<div class="readonly-field">{{ object.transponder.uplink|default:"-" }}</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">Полоса (МГц):</label>
<div class="readonly-field">{{ object.transponder.frequency_range|default:"-" }}</div>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">Перенос (МГц):</label>
<div class="readonly-field">{{ object.transponder.transfer|default:"-" }}</div>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">Поляризация:</label>
<div class="readonly-field">{{ object.transponder.polarization.name|default:"-" }}</div>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">ОСШ (дБ):</label>
<div class="readonly-field">{{ object.transponder.snr|default:"-" }}</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Зона покрытия:</label>
<div class="readonly-field">{{ object.transponder.zone_name|default:"-" }}</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Дата создания:</label>
<div class="readonly-field">
{% if object.transponder.created_at %}{{ object.transponder.created_at|date:"d.m.Y H:i" }}{% else %}-{% endif %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Создан пользователем:</label>
<div class="readonly-field">
{% if object.transponder.created_by %}{{ object.transponder.created_by }}{% else %}-{% endif %}
</div>
</div>
</div>
</div>
{% else %}
<div class="mb-3">
<p class="text-muted">Нет данных о транспондере</p>
</div>
{% endif %}
</div>
<!-- Блок с картой -->
<div class="form-section">
<div class="form-section-header">
<h4>Карта</h4>
</div>
<div class="map-container">
<div id="map"></div>
</div>
</div>
<!-- Геоданные -->
<div class="form-section">
<div class="form-section-header">
<h4>Геоданные</h4>
</div>
<!-- Координаты геолокации -->
<div class="coord-sync-group">
<div class="coord-group-header">Координаты геолокации</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="id_geo_latitude" class="form-label">Широта:</label>
<input type="number" step="0.000001" class="form-control" id="id_geo_latitude"
name="geo_latitude"
value="{% if object.geo_obj and object.geo_obj.coords %}{{ object.geo_obj.coords.y|unlocalize }}{% endif %}">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="id_geo_longitude" class="form-label">Долгота:</label>
<input type="number" step="0.000001" class="form-control" id="id_geo_longitude"
name="geo_longitude"
value="{% if object.geo_obj and object.geo_obj.coords %}{{ object.geo_obj.coords.x|unlocalize }}{% endif %}">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
{% include 'mainapp/components/_form_field.html' with field=geo_form.location %}
</div>
<div class="col-md-6">
{% include 'mainapp/components/_form_field.html' with field=geo_form.comment %}
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Дата и время:</label>
<div class="datetime-group">
<div>
<label for="id_timestamp_date" class="form-label">Дата:</label>
<input type="date" class="form-control" id="id_timestamp_date" name="timestamp_date"
value="{% if object.geo_obj and object.geo_obj.timestamp %}{{ object.geo_obj.timestamp|date:'Y-m-d' }}{% endif %}">
</div>
<div>
<label for="id_timestamp_time" class="form-label">Время:</label>
<input type="time" class="form-control" id="id_timestamp_time" name="timestamp_time"
value="{% if object.geo_obj and object.geo_obj.timestamp %}{{ object.geo_obj.timestamp|time:'H:i' }}{% endif %}">
</div>
</div>
</div>
</div>
<div class="col-md-6">
{% include 'mainapp/components/_form_field.html' with field=geo_form.is_average %}
</div>
</div>
<div class="row">
<div class="col-md-12">
{% include 'mainapp/components/_form_field.html' with field=geo_form.mirrors %}
</div>
</div>
</div>
</form>
</div>
{% endblock %}
{% block extra_js %}
{{ block.super }}
<!-- Подключаем Leaflet и его плагины -->
{% leaflet_js %}
{% leaflet_css %}
<script src="{% static 'leaflet-markers/js/leaflet-color-markers.js' %}"></script>
<!-- Подключаем кастомный виджет для мультивыбора -->
<script src="{% static 'js/checkbox-select-multiple.js' %}"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Инициализация карты
const map = L.map('map').setView([55.75, 37.62], 5);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// Функция для создания иконки маркера
function createMarkerIcon() {
return L.icon({
iconUrl: '{% static "leaflet-markers/img/marker-icon-blue.png" %}',
shadowUrl: `{% static 'leaflet-markers/img/marker-shadow.png' %}`,
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
}
const editableLayerGroup = new L.FeatureGroup();
map.addLayer(editableLayerGroup);
// Маркер геолокации
const marker = L.marker([55.75, 37.62], {
draggable: false,
icon: createMarkerIcon(),
title: 'Геолокация'
}).addTo(editableLayerGroup);
marker.bindPopup('Геолокация');
// Синхронизация при изменении формы
function syncFromForm() {
const lat = parseFloat(document.getElementById('id_geo_latitude').value);
const lng = parseFloat(document.getElementById('id_geo_longitude').value);
if (!isNaN(lat) && !isNaN(lng)) {
marker.setLatLng([lat, lng]);
}
}
// Синхронизация при перетаскивании (только если активировано)
marker.on('dragend', function (event) {
const latLng = event.target.getLatLng();
document.getElementById('id_geo_latitude').value = latLng.lat.toFixed(6);
document.getElementById('id_geo_longitude').value = latLng.lng.toFixed(6);
});
// Добавляем методы для управления
marker.enableEditing = function () {
this.dragging.enable();
this.openPopup();
};
marker.disableEditing = function () {
this.dragging.disable();
this.closePopup();
};
marker.syncFromForm = syncFromForm;
// Устанавливаем начальные координаты из полей формы
function initMarkersFromForm() {
const geoLat = parseFloat(document.getElementById('id_geo_latitude').value) || 55.75;
const geoLng = parseFloat(document.getElementById('id_geo_longitude').value) || 37.62;
marker.setLatLng([geoLat, geoLng]);
// Центрируем карту на маркере
map.setView(marker.getLatLng(), 10);
}
// Настройка формы для синхронизации с маркером
function setupFormChange(latFieldId, lngFieldId, marker) {
const latField = document.getElementById(latFieldId);
const lngField = document.getElementById(lngFieldId);
[latField, lngField].forEach(field => {
field.addEventListener('change', function () {
const lat = parseFloat(latField.value);
const lng = parseFloat(lngField.value);
if (!isNaN(lat) && !isNaN(lng)) {
marker.setLatLng([lat, lng]);
map.setView(marker.getLatLng(), 10);
}
});
});
}
// Инициализация
initMarkersFromForm();
// Настройка формы для синхронизации с маркером
setupFormChange('id_geo_latitude', 'id_geo_longitude', marker);
// --- УПРАВЛЕНИЕ РЕДАКТИРОВАНИЕМ ---
// Кнопки редактирования
const editControlsDiv = L.DomUtil.create('div', 'map-controls');
editControlsDiv.style.position = 'absolute';
editControlsDiv.style.top = '10px';
editControlsDiv.style.right = '10px';
editControlsDiv.style.zIndex = '1000';
editControlsDiv.style.background = 'white';
editControlsDiv.style.padding = '10px';
editControlsDiv.style.borderRadius = '4px';
editControlsDiv.style.boxShadow = '0 0 10px rgba(0,0,0,0.2)';
editControlsDiv.innerHTML = `
<div class="map-controls">
<button type="button" id="edit-btn" class="map-control-btn edit">Редактировать</button>
<button type="button" id="save-btn" class="map-control-btn save" disabled>Сохранить</button>
<button type="button" id="cancel-btn" class="map-control-btn cancel" disabled>Отмена</button>
</div>
`;
map.getContainer().appendChild(editControlsDiv);
let isEditing = false;
// Сохраняем начальные координаты для отмены
const initialPosition = marker.getLatLng();
// Включение редактирования
document.getElementById('edit-btn').addEventListener('click', function () {
if (isEditing) return;
isEditing = true;
document.getElementById('edit-btn').classList.add('active');
document.getElementById('save-btn').disabled = false;
document.getElementById('cancel-btn').disabled = false;
// Включаем drag для маркера
marker.enableEditing();
// Показываем подсказку
L.popup()
.setLatLng(map.getCenter())
.setContent('Перетаскивайте маркер. Нажмите "Сохранить" или "Отмена".')
.openOn(map);
});
// Сохранение изменений
document.getElementById('save-btn').addEventListener('click', function () {
if (!isEditing) return;
isEditing = false;
document.getElementById('edit-btn').classList.remove('active');
document.getElementById('save-btn').disabled = true;
document.getElementById('cancel-btn').disabled = true;
// Отключаем редактирование
marker.disableEditing();
// Обновляем начальную позицию
initialPosition.lat = marker.getLatLng().lat;
initialPosition.lng = marker.getLatLng().lng;
// Убираем попап подсказки
map.closePopup();
});
// Отмена изменений
document.getElementById('cancel-btn').addEventListener('click', function () {
if (!isEditing) return;
isEditing = false;
document.getElementById('edit-btn').classList.remove('active');
document.getElementById('save-btn').disabled = true;
document.getElementById('cancel-btn').disabled = true;
// Возвращаем маркер на исходную позицию
marker.setLatLng(initialPosition);
// Отключаем редактирование
marker.disableEditing();
// Синхронизируем форму с исходным значением
document.getElementById('id_geo_latitude').value = initialPosition.lat.toFixed(6);
document.getElementById('id_geo_longitude').value = initialPosition.lng.toFixed(6);
map.closePopup();
});
// Легенда
const legend = L.control({ position: 'bottomright' });
legend.onAdd = function () {
const div = L.DomUtil.create('div', 'info legend');
div.style.fontSize = '14px';
div.style.backgroundColor = 'white';
div.style.padding = '10px';
div.style.borderRadius = '4px';
div.style.boxShadow = '0 0 10px rgba(0,0,0,0.2)';
div.innerHTML = `
<h5>Легенда</h5>
<div><span style="color: blue; font-weight: bold;">•</span> Геолокация</div>
`;
return div;
};
legend.addTo(map);
});
</script>
{% endblock %}