Добавил теханализ
This commit is contained in:
@@ -49,10 +49,13 @@
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" title="Показать на карте"
|
||||
<button type="button" class="btn btn-primary btn-sm" title="Показать на карте"
|
||||
onclick="showSelectedOnMap()">
|
||||
<i class="bi bi-map"></i> Карта
|
||||
</button>
|
||||
<a href="{% url 'mainapp:tech_analyze_entry' %}" class="btn btn-info btn-sm" title="Тех. анализ">
|
||||
<i class="bi bi-clipboard-data"></i> Тех. анализ
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Items per page select moved here -->
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'mainapp:data_entry' %}" class="btn btn-info btn-sm" title="Ввод данных точек спутников">
|
||||
<i class="bi bi-keyboard"></i> Ввод данных
|
||||
Передача точек
|
||||
</a>
|
||||
<a href="{% url 'mainapp:load_excel_data' %}" class="btn btn-primary btn-sm" title="Загрузка данных из Excel">
|
||||
<i class="bi bi-file-earmark-excel"></i> Excel
|
||||
@@ -94,7 +94,7 @@
|
||||
<i class="bi bi-trash"></i> Удалить
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" title="Показать на карте"
|
||||
<button type="button" class="btn btn-primary btn-sm" title="Показать на карте"
|
||||
onclick="showSelectedOnMap()">
|
||||
<i class="bi bi-map"></i> Карта
|
||||
</button>
|
||||
|
||||
343
dbapp/mainapp/templates/mainapp/tech_analyze_entry.html
Normal file
343
dbapp/mainapp/templates/mainapp/tech_analyze_entry.html
Normal file
@@ -0,0 +1,343 @@
|
||||
{% extends "mainapp/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Тех. анализ - Ввод данных{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link href="https://unpkg.com/tabulator-tables@6.2.5/dist/css/tabulator_bootstrap5.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.data-entry-container {
|
||||
padding: 20px;
|
||||
}
|
||||
.form-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
#tech-analyze-table {
|
||||
margin-top: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
#tech-analyze-table .tabulator-header {
|
||||
font-size: 12px;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#tech-analyze-table .tabulator-header .tabulator-col {
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
height: auto;
|
||||
min-height: 40px;
|
||||
}
|
||||
#tech-analyze-table .tabulator-header .tabulator-col-content {
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
padding: 6px 4px;
|
||||
}
|
||||
#tech-analyze-table .tabulator-cell {
|
||||
font-size: 12px;
|
||||
padding: 6px 4px;
|
||||
}
|
||||
.btn-group-custom {
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="data-entry-container">
|
||||
<h2>Тех. анализ - Ввод данных</h2>
|
||||
|
||||
<div class="form-section">
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="satellite-select" class="form-label">Спутник <span class="text-danger">*</span></label>
|
||||
<select id="satellite-select" class="form-select">
|
||||
<option value="">Выберите спутник</option>
|
||||
{% for satellite in satellites %}
|
||||
<option value="{{ satellite.id }}">{{ satellite.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<!-- <div class="col-md-8 mb-3">
|
||||
<div class="alert alert-info mb-0">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
<strong>Инструкция:</strong>
|
||||
<ul class="mb-0 mt-2" style="font-size: 0.9em;">
|
||||
<li><strong>Порядок столбцов в Excel:</strong> Имя, Частота МГц, Полоса МГц, Сим. скорость БОД, Модуляция, Стандарт, Примечание</li>
|
||||
<li><strong>Поляризация извлекается автоматически</strong> из имени (например: "Сигнал 11500 МГц L" → "Левая")</li>
|
||||
<li>Поддерживаемые буквы: L=Левая, R=Правая, H=Горизонтальная, V=Вертикальная</li>
|
||||
<li>Скопируйте данные из Excel и вставьте в таблицу (Ctrl+V)</li>
|
||||
<li>Используйте стрелки, Tab, Enter для навигации и редактирования</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5>Таблица данных <span id="row-count" class="badge bg-primary">0</span></h5>
|
||||
</div>
|
||||
<div class="btn-group-custom">
|
||||
<button id="add-row" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Добавить строку
|
||||
</button>
|
||||
<button id="delete-selected" class="btn btn-warning ms-2">
|
||||
<i class="bi bi-trash"></i> Удалить выбранные
|
||||
</button>
|
||||
<button id="save-data" class="btn btn-success ms-2">
|
||||
<i class="bi bi-save"></i> Сохранить
|
||||
</button>
|
||||
<button id="clear-table" class="btn btn-danger ms-2">
|
||||
<i class="bi bi-x-circle"></i> Очистить таблицу
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tech-analyze-table"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://unpkg.com/tabulator-tables@6.2.5/dist/js/tabulator.min.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize Tabulator
|
||||
const table = new Tabulator("#tech-analyze-table", {
|
||||
layout: "fitDataStretch",
|
||||
height: "500px",
|
||||
placeholder: "Нет данных. Скопируйте данные из Excel и вставьте в таблицу (Ctrl+V).",
|
||||
headerWordWrap: true,
|
||||
clipboard: true,
|
||||
clipboardPasteAction: "replace",
|
||||
clipboardPasteParser: function(clipboard) {
|
||||
// Парсим данные из буфера обмена
|
||||
const rows = clipboard.split('\n').filter(row => row.trim() !== '');
|
||||
const data = [];
|
||||
|
||||
// Функция для извлечения поляризации из имени
|
||||
function extractPolarization(name) {
|
||||
if (!name) return '';
|
||||
|
||||
// Маппинг букв на полные названия
|
||||
const polarizationMap = {
|
||||
'L': 'Левая',
|
||||
'R': 'Правая',
|
||||
'H': 'Горизонтальная',
|
||||
'V': 'Вертикальная'
|
||||
};
|
||||
|
||||
// Ищем паттерн "МГц X" где X - буква поляризации
|
||||
const match = name.match(/МГц\s+([LRHV])/i);
|
||||
if (match) {
|
||||
const letter = match[1].toUpperCase();
|
||||
return polarizationMap[letter] || '';
|
||||
}
|
||||
|
||||
// Альтернативный паттерн: просто последняя буква L/R/H/V
|
||||
const lastChar = name.trim().slice(-1).toUpperCase();
|
||||
if (polarizationMap[lastChar]) {
|
||||
return polarizationMap[lastChar];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
rows.forEach(row => {
|
||||
// Разделяем по табуляции (стандартный разделитель Excel)
|
||||
const cells = row.split('\t');
|
||||
|
||||
const name = cells[0] || '';
|
||||
const polarization = extractPolarization(name);
|
||||
|
||||
// Создаем объект с правильными полями (новый порядок без поляризации в начале)
|
||||
const rowData = {
|
||||
name: name,
|
||||
frequency: cells[1] || '',
|
||||
freq_range: cells[2] || '',
|
||||
bod_velocity: cells[3] || '',
|
||||
modulation: cells[4] || '',
|
||||
standard: cells[5] || '',
|
||||
note: cells[6] || '',
|
||||
polarization: polarization // Автоматически извлеченная поляризация
|
||||
};
|
||||
|
||||
data.push(rowData);
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
formatter: "rowSelection",
|
||||
titleFormatter: "rowSelection",
|
||||
hozAlign: "center",
|
||||
headerSort: false,
|
||||
width: 40,
|
||||
clipboard: false,
|
||||
cellClick: function(e, cell) {
|
||||
cell.getRow().toggleSelect();
|
||||
}
|
||||
},
|
||||
{title: "Имя", field: "name", minWidth: 150, widthGrow: 2, editor: "input"},
|
||||
{title: "Частота, МГц", field: "frequency", minWidth: 100, widthGrow: 1, editor: "input"},
|
||||
{title: "Полоса, МГц", field: "freq_range", minWidth: 100, widthGrow: 1, editor: "input"},
|
||||
{title: "Символьная скорость, БОД", field: "bod_velocity", minWidth: 150, widthGrow: 1.5, editor: "input"},
|
||||
{title: "Вид модуляции", field: "modulation", minWidth: 120, widthGrow: 1.2, editor: "input"},
|
||||
{title: "Стандарт", field: "standard", minWidth: 100, widthGrow: 1, editor: "input"},
|
||||
{title: "Примечание", field: "note", minWidth: 150, widthGrow: 2, editor: "input"},
|
||||
{title: "Поляризация", field: "polarization", minWidth: 100, widthGrow: 1, editor: "input"},
|
||||
],
|
||||
data: [],
|
||||
});
|
||||
|
||||
// Update row count
|
||||
function updateRowCount() {
|
||||
const count = table.getDataCount();
|
||||
document.getElementById('row-count').textContent = count;
|
||||
}
|
||||
|
||||
// Listen to table events
|
||||
table.on("rowAdded", updateRowCount);
|
||||
table.on("dataChanged", updateRowCount);
|
||||
table.on("rowDeleted", updateRowCount);
|
||||
|
||||
// Add row button
|
||||
document.getElementById('add-row').addEventListener('click', function() {
|
||||
table.addRow({
|
||||
name: '',
|
||||
frequency: '',
|
||||
freq_range: '',
|
||||
bod_velocity: '',
|
||||
modulation: '',
|
||||
standard: '',
|
||||
note: '',
|
||||
polarization: ''
|
||||
});
|
||||
});
|
||||
|
||||
// Delete selected rows
|
||||
document.getElementById('delete-selected').addEventListener('click', function() {
|
||||
const selectedRows = table.getSelectedRows();
|
||||
if (selectedRows.length === 0) {
|
||||
alert('Выберите строки для удаления');
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm(`Удалить ${selectedRows.length} строк(и)?`)) {
|
||||
selectedRows.forEach(row => row.delete());
|
||||
}
|
||||
});
|
||||
|
||||
// Save data
|
||||
document.getElementById('save-data').addEventListener('click', async function() {
|
||||
const satelliteId = document.getElementById('satellite-select').value;
|
||||
|
||||
if (!satelliteId) {
|
||||
alert('Пожалуйста, выберите спутник');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = table.getData();
|
||||
|
||||
if (data.length === 0) {
|
||||
alert('Нет данных для сохранения');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate that all rows have names
|
||||
const emptyNames = data.filter(row => !row.name || row.name.trim() === '');
|
||||
if (emptyNames.length > 0) {
|
||||
alert('Все строки должны иметь имя');
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable button while saving
|
||||
this.disabled = true;
|
||||
this.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Сохранение...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/tech-analyze/save/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
satellite_id: satelliteId,
|
||||
rows: data
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
let message = `Успешно сохранено!\n`;
|
||||
message += `Создано: ${result.created}\n`;
|
||||
message += `Обновлено: ${result.updated}\n`;
|
||||
message += `Всего: ${result.total}`;
|
||||
|
||||
if (result.errors && result.errors.length > 0) {
|
||||
message += `\n\nОшибки:\n${result.errors.join('\n')}`;
|
||||
}
|
||||
|
||||
alert(message);
|
||||
|
||||
// Clear table after successful save
|
||||
if (!result.errors || result.errors.length === 0) {
|
||||
table.clearData();
|
||||
}
|
||||
} else {
|
||||
alert('Ошибка: ' + (result.error || 'Неизвестная ошибка'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Произошла ошибка при сохранении данных');
|
||||
} finally {
|
||||
// Re-enable button
|
||||
this.disabled = false;
|
||||
this.innerHTML = '<i class="bi bi-save"></i> Сохранить';
|
||||
}
|
||||
});
|
||||
|
||||
// Clear table
|
||||
document.getElementById('clear-table').addEventListener('click', function() {
|
||||
if (confirm('Вы уверены, что хотите очистить таблицу?')) {
|
||||
table.clearData();
|
||||
updateRowCount();
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to get CSRF token
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
// Initialize row count
|
||||
updateRowCount();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user