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

603 lines
26 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 %}
<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));
}
</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>
<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="coord-group">
<div class="coord-group-header">Координаты Кубсата</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="id_kupsat_longitude" class="form-label">Долгота:</label>
<input type="number" step="0.000001" class="form-control"
id="id_kupsat_longitude" name="kupsat_longitude"
value="{% if object.geo_obj and object.geo_obj.coords_kupsat %}{{ object.geo_obj.coords_kupsat.x|unlocalize }}{% endif %}">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="id_kupsat_latitude" class="form-label">Широта:</label>
<input type="number" step="0.000001" class="form-control"
id="id_kupsat_latitude" name="kupsat_latitude"
value="{% if object.geo_obj and object.geo_obj.coords_kupsat %}{{ object.geo_obj.coords_kupsat.y|unlocalize }}{% endif %}">
</div>
</div>
</div>
</div>
<!-- Координаты оперативников -->
<div class="coord-group">
<div class="coord-group-header">Координаты оперативников</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="id_valid_longitude" class="form-label">Долгота:</label>
<input type="number" step="0.000001" class="form-control"
id="id_valid_longitude" name="valid_longitude"
value="{% if object.geo_obj and object.geo_obj.coords_valid %}{{ object.geo_obj.coords_valid.x|unlocalize }}{% endif %}">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="id_valid_latitude" class="form-label">Широта:</label>
<input type="number" step="0.000001" class="form-control"
id="id_valid_latitude" name="valid_latitude"
value="{% if object.geo_obj and object.geo_obj.coords_valid %}{{ object.geo_obj.coords_valid.y|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>
{% if object.geo_obj %}
<div class="row mt-3">
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Расстояние гео-кубсат, км:</label>
<div class="readonly-field">
{% if object.geo_obj.distance_coords_kup is not None %}
{{ object.geo_obj.distance_coords_kup|floatformat:2 }}
{% else %}
-
{% endif %}
</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Расстояние гео-опер, км:</label>
<div class="readonly-field">
{% if object.geo_obj.distance_coords_valid is not None %}
{{ object.geo_obj.distance_coords_valid|floatformat:2 }}
{% else %}
-
{% endif %}
</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Расстояние кубсат-опер, км:</label>
<div class="readonly-field">
{% if object.geo_obj.distance_kup_valid is not None %}
{{ object.geo_obj.distance_kup_valid|floatformat:2 }}
{% else %}
-
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
</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>
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);
// Определяем цвета для маркеров
const colors = {
geo: 'blue',
kupsat: 'red',
valid: 'green'
};
// Функция для создания иконки маркера
function createMarkerIcon(color) {
return L.icon({
iconUrl: '{% static "leaflet-markers/img/marker-icon-" %}' + color + '.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 markers = {};
function createMarker(latFieldId, lngFieldId, position, color, name) {
const marker = L.marker(position, {
draggable: false,
icon: createMarkerIcon(color),
title: name
}).addTo(editableLayerGroup);
marker.bindPopup(name);
// Синхронизация при изменении формы
function syncFromForm() {
const lat = parseFloat(document.getElementById(latFieldId).value);
const lng = parseFloat(document.getElementById(lngFieldId).value);
if (!isNaN(lat) && !isNaN(lng)) {
marker.setLatLng([lat, lng]);
}
}
// Синхронизация при перетаскивании (только если активировано)
marker.on('dragend', function(event) {
const latLng = event.target.getLatLng();
document.getElementById(latFieldId).value = latLng.lat.toFixed(6);
document.getElementById(lngFieldId).value = latLng.lng.toFixed(6);
});
// Добавляем методы для управления
marker.enableEditing = function() {
this.dragging.enable();
this.openPopup();
};
marker.disableEditing = function() {
this.dragging.disable();
this.closePopup();
};
marker.syncFromForm = syncFromForm;
return marker;
}
// Создаем маркеры
markers.geo = createMarker(
'id_geo_latitude',
'id_geo_longitude',
[55.75, 37.62],
colors.geo,
'Геолокация'
);
markers.kupsat = createMarker(
'id_kupsat_latitude',
'id_kupsat_longitude',
[55.75, 37.61],
colors.kupsat,
'Кубсат'
);
markers.valid = createMarker(
'id_valid_latitude',
'id_valid_longitude',
[55.75, 37.63],
colors.valid,
'Оперативник'
);
// Устанавливаем начальные координаты из полей формы
function initMarkersFromForm() {
const geoLat = parseFloat(document.getElementById('id_geo_latitude').value) || 55.75;
const geoLng = parseFloat(document.getElementById('id_geo_longitude').value) || 37.62;
markers.geo.setLatLng([geoLat, geoLng]);
const kupsatLat = parseFloat(document.getElementById('id_kupsat_latitude').value) || 55.75;
const kupsatLng = parseFloat(document.getElementById('id_kupsat_longitude').value) || 37.61;
markers.kupsat.setLatLng([kupsatLat, kupsatLng]);
const validLat = parseFloat(document.getElementById('id_valid_latitude').value) || 55.75;
const validLng = parseFloat(document.getElementById('id_valid_longitude').value) || 37.63;
markers.valid.setLatLng([validLat, validLng]);
// Центрируем карту на первом маркере
map.setView(markers.geo.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', markers.geo);
setupFormChange('id_kupsat_latitude', 'id_kupsat_longitude', markers.kupsat);
setupFormChange('id_valid_latitude', 'id_valid_longitude', markers.valid);
// --- УПРАВЛЕНИЕ РЕДАКТИРОВАНИЕМ ---
// Кнопки редактирования
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 initialPositions = {
geo: markers.geo.getLatLng(),
kupsat: markers.kupsat.getLatLng(),
valid: markers.valid.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 для всех маркеров
Object.values(markers).forEach(marker => {
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;
// Отключаем редактирование
Object.values(markers).forEach(marker => {
marker.disableEditing();
});
// Обновляем начальные позиции
initialPositions.geo = markers.geo.getLatLng();
initialPositions.kupsat = markers.kupsat.getLatLng();
initialPositions.valid = markers.valid.getLatLng();
// Убираем попап подсказки
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;
// Возвращаем маркеры на исходные позиции
markers.geo.setLatLng(initialPositions.geo);
markers.kupsat.setLatLng(initialPositions.kupsat);
markers.valid.setLatLng(initialPositions.valid);
// Отключаем редактирование
Object.values(markers).forEach(marker => {
marker.disableEditing();
});
// Синхронизируем форму с исходными значениями
document.getElementById('id_geo_latitude').value = initialPositions.geo.lat.toFixed(6);
document.getElementById('id_geo_longitude').value = initialPositions.geo.lng.toFixed(6);
document.getElementById('id_kupsat_latitude').value = initialPositions.kupsat.lat.toFixed(6);
document.getElementById('id_kupsat_longitude').value = initialPositions.kupsat.lng.toFixed(6);
document.getElementById('id_valid_latitude').value = initialPositions.valid.lat.toFixed(6);
document.getElementById('id_valid_longitude').value = initialPositions.valid.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>
<div><span style="color: red; font-weight: bold;">•</span> Кубсат</div>
<div><span style="color: green; font-weight: bold;">•</span> Оперативники</div>
`;
return div;
};
legend.addTo(map);
});
</script>
{% endblock %}