Сортировка в таблицах и попытка приструнить генерацию превьюшек
All checks were successful
Deploy MES Core / deploy (push) Successful in 13s
All checks were successful
Deploy MES Core / deploy (push) Successful in 13s
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<table class="table table-hover mb-0 align-middle" data-sortable="1">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Сделка</th>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<table class="table table-hover mb-0 align-middle" data-sortable="1">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Заказчик</th>
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
<strong>DXF</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3" id="jobBox" {% if not last_job %}style="display:none"{% endif %}>
|
||||
<div class="small text-muted">Статус: <span id="jobStatus">{% if last_job %}{{ last_job.get_status_display }}{% endif %}</span></div>
|
||||
<div class="small text-muted">Обработано: <span id="jobProcessed">{% if last_job %}{{ last_job.processed }}{% endif %}</span>/<span id="jobTotal">{% if last_job %}{{ last_job.total }}{% endif %}</span></div>
|
||||
<div class="small text-muted">Обновлено: <span id="jobUpdated">{% if last_job %}{{ last_job.updated }}{% endif %}</span> · Пропущено: <span id="jobSkipped">{% if last_job %}{{ last_job.skipped }}{% endif %}</span> · Ошибок: <span id="jobErrors">{% if last_job %}{{ last_job.errors }}{% endif %}</span></div>
|
||||
<div class="small text-muted" id="jobMessage">{% if last_job %}{{ last_job.last_message }}{% endif %}</div>
|
||||
</div>
|
||||
|
||||
<form method="post" class="row g-3 align-items-end">
|
||||
{% csrf_token %}
|
||||
|
||||
@@ -42,6 +49,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="small text-muted">Таймаут на 1 DXF (сек)</label>
|
||||
<input type="number" step="1" min="5" name="per_task_timeout_sec" class="form-control border-secondary" value="{{ dxf_settings.per_task_timeout_sec|default_if_none:45|unlocalize }}">
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-outline-accent" name="action" value="save_settings">
|
||||
<i class="bi bi-save me-2"></i>Сохранить настройки
|
||||
@@ -49,6 +61,9 @@
|
||||
<button type="submit" class="btn btn-outline-accent" name="action" value="update_previews">
|
||||
<i class="bi bi-arrow-repeat me-2"></i>Обновить превьюшки DXF
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-warning" name="action" value="cancel_job">
|
||||
<i class="bi bi-stop-circle me-2"></i>Прервать
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
@@ -67,6 +82,44 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const box = document.getElementById('jobBox');
|
||||
const statusEl = document.getElementById('jobStatus');
|
||||
const totalEl = document.getElementById('jobTotal');
|
||||
const processedEl = document.getElementById('jobProcessed');
|
||||
const updatedEl = document.getElementById('jobUpdated');
|
||||
const skippedEl = document.getElementById('jobSkipped');
|
||||
const errorsEl = document.getElementById('jobErrors');
|
||||
const msgEl = document.getElementById('jobMessage');
|
||||
|
||||
async function tick() {
|
||||
try {
|
||||
const res = await fetch('{% url "maintenance_status" %}', { method: 'GET', headers: { 'Accept': 'application/json' } });
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
if (!data || !data.job) return;
|
||||
|
||||
if (box) box.style.display = '';
|
||||
if (statusEl) statusEl.textContent = data.job.status_label || data.job.status || '';
|
||||
if (totalEl) totalEl.textContent = String(data.job.total ?? '');
|
||||
if (processedEl) processedEl.textContent = String(data.job.processed ?? '');
|
||||
if (updatedEl) updatedEl.textContent = String(data.job.updated ?? '');
|
||||
if (skippedEl) skippedEl.textContent = String(data.job.skipped ?? '');
|
||||
if (errorsEl) errorsEl.textContent = String(data.job.errors ?? '');
|
||||
if (msgEl) msgEl.textContent = data.job.last_message || '';
|
||||
|
||||
if (data.job.status === 'running' || data.job.status === 'queued') {
|
||||
setTimeout(tick, 3000);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(tick, 400);
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,18 +1,18 @@
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<table class="table table-hover mb-0 align-middle" data-sortable="1">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Дата</th>
|
||||
<th data-sort-type="date">Дата</th>
|
||||
<th>Сделка</th>
|
||||
<th>Станок</th>
|
||||
<th>Наименование</th>
|
||||
<th>Габариты</th>
|
||||
<th style="width: 160px;">Прогресс</th>
|
||||
<th>План / Факт</th>
|
||||
<th data-sort-type="number">Габариты</th>
|
||||
<th data-sort="false" style="width: 160px;">Прогресс</th>
|
||||
<th data-sort-type="number">План / Факт</th>
|
||||
<th>Материал</th>
|
||||
<th class="text-center">Файлы</th>
|
||||
<th class="text-center">1С</th>
|
||||
<th data-sort="false" class="text-center">Файлы</th>
|
||||
<th data-sort="false" class="text-center">1С</th>
|
||||
<th>Статус</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<table class="table table-hover mb-0 align-middle" data-sortable="1">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Сделка</th>
|
||||
|
||||
@@ -30,17 +30,17 @@
|
||||
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<table class="table table-hover mb-0 align-middle" data-sortable="1">
|
||||
<thead>
|
||||
<tr class="table-custom-header">
|
||||
<th>Деталь</th>
|
||||
<th>Материал</th>
|
||||
<th>Размер</th>
|
||||
<th style="width: 160px;">Прогресс</th>
|
||||
<th data-sort="false" style="width: 160px;">Прогресс</th>
|
||||
<th class="text-center">Надо / Сделано / В плане</th>
|
||||
<th class="text-center">Осталось</th>
|
||||
<th class="text-center">Файлы</th>
|
||||
<th class="text-end">Действия</th>
|
||||
<th data-sort="false" class="text-center">Файлы</th>
|
||||
<th data-sort="false" class="text-end">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -312,6 +312,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const drawingFile = document.getElementById('id_drawing_file');
|
||||
const extraDrawing = document.getElementById('id_extra_drawing');
|
||||
const drawingName = document.getElementById('id_drawing_name');
|
||||
const qtyOrdered = document.getElementById('id_quantity_ordered');
|
||||
const sizeValue = document.getElementById('id_size_value');
|
||||
|
||||
const fillFromSource = document.getElementById('fillNameFromSource');
|
||||
const fillFromPdf = document.getElementById('fillNameFromPdf');
|
||||
@@ -378,12 +380,46 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (fillFromPdf) fillFromPdf.disabled = !hasPdf;
|
||||
}
|
||||
|
||||
function tryFillFromDxfFilename(filename) {
|
||||
if (!filename) return false;
|
||||
|
||||
const isDxf = filename.toLowerCase().endsWith('.dxf');
|
||||
if (!isDxf) return false;
|
||||
|
||||
// Поддерживаем паттерн в имени файла вида "-s2n45".
|
||||
// На практике встречаются варианты: "s2n45", "-s2_n45", "_s2-n45" и т.п.
|
||||
// Поэтому ищем "s<число> ... n<целое>" в любом месте имени, разрешая разделители между ними.
|
||||
const base = filenameBase(filename);
|
||||
const normalized = base.replace(/\s+/g, '');
|
||||
const m = normalized.match(/s([0-9]+(?:[\.,][0-9]+)?)[^0-9a-zA-Z]*n([0-9]+)/);
|
||||
if (!m) return false;
|
||||
|
||||
const s = (m[1] || '').replace(',', '.');
|
||||
const n = (m[2] || '').trim();
|
||||
|
||||
// По твоей просьбе: перезатираем значения, даже если пользователь что-то уже вводил.
|
||||
if (sizeValue) {
|
||||
sizeValue.value = s;
|
||||
sizeValue.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
if (qtyOrdered) {
|
||||
qtyOrdered.value = n;
|
||||
qtyOrdered.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (drawingFile) drawingFile.addEventListener('change', function () {
|
||||
updateFileButtons();
|
||||
if (!drawingName) return;
|
||||
if (drawingName.value && drawingName.value.trim() !== '') return;
|
||||
if (!drawingFile.files || drawingFile.files.length === 0) return;
|
||||
drawingName.value = filenameBase(drawingFile.files[0].name);
|
||||
|
||||
const fname = drawingFile.files[0].name;
|
||||
|
||||
// По твоей просьбе: при выборе исходника перезаполняем «Наименование» из имени файла.
|
||||
if (drawingName) drawingName.value = filenameBase(fname);
|
||||
|
||||
// Пытаемся распарсить «Размер» и «Требуется» из имени DXF.
|
||||
tryFillFromDxfFilename(fname);
|
||||
});
|
||||
if (extraDrawing) extraDrawing.addEventListener('change', updateFileButtons);
|
||||
updateFileButtons();
|
||||
@@ -391,8 +427,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (fillFromSource) {
|
||||
fillFromSource.addEventListener('click', function () {
|
||||
if (!drawingFile || !drawingFile.files || drawingFile.files.length === 0) return;
|
||||
const base = filenameBase(drawingFile.files[0].name);
|
||||
const fname = drawingFile.files[0].name;
|
||||
const base = filenameBase(fname);
|
||||
|
||||
// Кнопка также перезаполняет значения из имени исходника.
|
||||
if (drawingName) drawingName.value = base;
|
||||
tryFillFromDxfFilename(fname);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user