Добавил поис на странице реестра добавил еск для сброса фильтров
All checks were successful
Deploy MES Core / deploy (push) Successful in 11s

This commit is contained in:
2026-04-16 22:58:20 +03:00
parent b48bf0947a
commit ef30e5ebb8
3 changed files with 78 additions and 14 deletions

View File

@@ -1,6 +1,6 @@
<div class="card border-secondary mb-3 shadow-sm"> <div class="card border-secondary mb-3 shadow-sm">
<div class="card-body py-2"> <div class="card-body py-2">
<form method="get" id="filter-form" class="row g-2 align-items-center"> <form method="get" id="filter-form" class="row g-1 align-items-center">
<input type="hidden" name="filtered" value="1"> <input type="hidden" name="filtered" value="1">
{% if user_role != 'operator' %} {% if user_role != 'operator' %}
@@ -53,8 +53,21 @@
<input type="date" name="end_date" class="form-control form-control-sm bg-body text-body border-secondary registry-filter-date" value="{{ end_date }}" onchange="this.form.submit()"> <input type="date" name="end_date" class="form-control form-control-sm bg-body text-body border-secondary registry-filter-date" value="{{ end_date }}" onchange="this.form.submit()">
</div> </div>
<div class="col-md-1 text-end mt-auto"> <div class="col-md-auto">
<a href="{% url 'registry' %}?reset=1" class="btn btn-outline-secondary btn-sm w-100" id="registryResetBtn" title="Сброс"> <label class="small text-muted mb-1 fw-bold">Поиск:</label>
<input
type="text"
name="q"
class="form-control form-control-sm bg-body text-body border-secondary registry-filter-q"
value="{{ q|default:'' }}"
placeholder="№ сделки / наименование / материал"
onkeydown="if(event.key==='Enter'){event.preventDefault(); this.form.submit();}"
>
</div>
<div class="col-md-auto text-end">
<label class="small text-muted mb-1 fw-bold d-block" style="visibility:hidden;">Сброс</label>
<a href="{% url 'registry' %}?reset=1" class="btn btn-outline-secondary btn-sm" id="registryResetBtn" title="Сброс">
<i class="bi bi-arrow-counterclockwise me-1"></i>Сброс <i class="bi bi-arrow-counterclockwise me-1"></i>Сброс
</a> </a>
</div> </div>
@@ -80,10 +93,12 @@
function saveFilters(){ function saveFilters(){
if (!form) return; if (!form) return;
const qEl = form.querySelector('input[name="q"]');
const data = { const data = {
statuses: Array.from(form.querySelectorAll('input[name="statuses"]:checked')).map(i=>i.value), statuses: Array.from(form.querySelectorAll('input[name="statuses"]:checked')).map(i=>i.value),
m_ids: Array.from(form.querySelectorAll('input[name="m_ids"]:checked')).map(i=>i.value), m_ids: Array.from(form.querySelectorAll('input[name="m_ids"]:checked')).map(i=>i.value),
start_date: s ? s.value : '' start_date: s ? s.value : '',
q: qEl ? (qEl.value || '') : ''
}; };
try { localStorage.setItem('registry_filters', JSON.stringify(data)); } catch(_){} try { localStorage.setItem('registry_filters', JSON.stringify(data)); } catch(_){}
} }
@@ -107,6 +122,9 @@
if (Array.isArray(data.m_ids)){ if (Array.isArray(data.m_ids)){
form.querySelectorAll('input[name="m_ids"]').forEach(i=>{ i.checked = data.m_ids.includes(i.value); }); form.querySelectorAll('input[name="m_ids"]').forEach(i=>{ i.checked = data.m_ids.includes(i.value); });
} }
const qEl = form.querySelector('input[name="q"]');
if (qEl) qEl.value = data.q || '';
if (s) s.value = data.start_date || weekAgo; if (s) s.value = data.start_date || weekAgo;
if (e) e.value = today; if (e) e.value = today;
const filtered = form.querySelector('input[name="filtered"]'); const filtered = form.querySelector('input[name="filtered"]');
@@ -125,6 +143,19 @@
}); });
} }
document.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
if (e.defaultPrevented) return;
const active = document.activeElement;
if (active && (active.tagName || '').toLowerCase() === 'textarea') return;
if (document.querySelector('.modal.show')) return;
if (resetBtn) {
e.preventDefault();
resetBtn.click();
}
}, true);
restoreFilters(); restoreFilters();
} }
}); });

View File

@@ -435,6 +435,8 @@ class RegistryView(LoginRequiredMixin, ListView):
# Диапазон дат, задаваемый пользователем. Если фильтры не активны или явно указан reset=1 — используем дефолты # Диапазон дат, задаваемый пользователем. Если фильтры не активны или явно указан reset=1 — используем дефолты
start_date = self.request.GET.get('start_date') start_date = self.request.GET.get('start_date')
end_date = self.request.GET.get('end_date') end_date = self.request.GET.get('end_date')
q = (self.request.GET.get('q') or '').strip()
# Дефолтный режим: последние 7 дней и только статус "В работе" # Дефолтный режим: последние 7 дней и только статус "В работе"
is_default = (not filtered) or bool(reset) is_default = (not filtered) or bool(reset)
@@ -451,6 +453,16 @@ class RegistryView(LoginRequiredMixin, ListView):
# Ограничения по ролям # Ограничения по ролям
if q:
queryset = queryset.filter(
Q(task__deal__number__icontains=q)
| Q(task__drawing_name__icontains=q)
| Q(task__entity__name__icontains=q)
| Q(task__entity__drawing_number__icontains=q)
| Q(task__material__name__icontains=q)
| Q(task__material__full_name__icontains=q)
)
if role == 'operator': if role == 'operator':
user_machines = profile.machines.all() if profile else Machine.objects.none() user_machines = profile.machines.all() if profile else Machine.objects.none()
queryset = queryset.filter(machine__in=user_machines) queryset = queryset.filter(machine__in=user_machines)
@@ -470,6 +482,7 @@ class RegistryView(LoginRequiredMixin, ListView):
context['user_role'] = role context['user_role'] = role
context['user_roles'] = sorted(roles) context['user_roles'] = sorted(roles)
context['is_readonly'] = bool(getattr(profile, 'is_readonly', False)) if profile else False context['is_readonly'] = bool(getattr(profile, 'is_readonly', False)) if profile else False
context['q'] = (self.request.GET.get('q') or '').strip()
allowed_ws = list(profile.allowed_workshops.values_list('id', flat=True)) if profile else [] allowed_ws = list(profile.allowed_workshops.values_list('id', flat=True)) if profile else []
context['allowed_workshop_ids'] = allowed_ws context['allowed_workshop_ids'] = allowed_ws
@@ -511,6 +524,16 @@ class RegistryView(LoginRequiredMixin, ListView):
work_qs = WorkItem.objects.select_related('deal', 'deal__company', 'entity', 'entity__planned_material', 'operation', 'machine', 'workshop') work_qs = WorkItem.objects.select_related('deal', 'deal__company', 'entity', 'entity__planned_material', 'operation', 'machine', 'workshop')
q = (self.request.GET.get('q') or '').strip()
if q:
work_qs = work_qs.filter(
Q(deal__number__icontains=q)
| Q(entity__name__icontains=q)
| Q(entity__drawing_number__icontains=q)
| Q(entity__planned_material__name__icontains=q)
| Q(entity__planned_material__full_name__icontains=q)
)
m_ids = [int(i) for i in self.request.GET.getlist('m_ids') if str(i).isdigit()] m_ids = [int(i) for i in self.request.GET.getlist('m_ids') if str(i).isdigit()]
if m_ids: if m_ids:
work_qs = work_qs.filter(Q(machine_id__in=m_ids) | Q(machine_id__isnull=True)) work_qs = work_qs.filter(Q(machine_id__in=m_ids) | Q(machine_id__isnull=True))

View File

@@ -161,6 +161,16 @@ body {
width: 120px; width: 120px;
} }
.registry-filter-q {
width: 220px;
}
@media (max-width: 575.98px) {
.registry-filter-q {
width: 100%;
}
}
/* Специальный класс для центрирования окна логина (вернем его только там) */ /* Специальный класс для центрирования окна логина (вернем его только там) */
.sf-attention { .sf-attention {
animation: sfAttentionPulse 1.6s ease-in-out infinite; animation: sfAttentionPulse 1.6s ease-in-out infinite;