fix: убрана двойная загрузка данных на странице списка сотрудников

- Упрощен employee_list view - НЕ загружает данные сотрудников
- Сервер отдает только справочники для фильтров
- Убран серверный рендеринг карточек из шаблона
- Добавлен spinner во время загрузки
- Данные загружаются один раз через JavaScript
- Производительность улучшена - нет двойного запроса
This commit is contained in:
admin i 2025-12-05 12:35:10 +03:00
parent 785a82f54b
commit e31972b7aa
2 changed files with 36 additions and 139 deletions

View File

@ -206,46 +206,37 @@ def employee_list(request):
Список сотрудников.
Если ?format=json — вернуть JSON (для автокомплита):
/employees/list/?format=json&q=...&exclude=1,2,3&limit=12
Для HTML страницы - данные загружаются через JavaScript на клиенте.
"""
api = get_api_client(request)
page = request.GET.get("page") or 1
ordering = request.GET.get("o") or "last_name"
q = (request.GET.get("q") or "").strip()
department = request.GET.get("department")
position = request.GET.get("position")
is_active = request.GET.get("is_active")
params = {"page": page, "ordering": ordering}
if q:
params["search"] = q
if department:
params["department"] = department
if position:
params["position"] = position
if is_active:
params["is_active"] = is_active
resp = api.get("v1/employees/", params=params)
if not resp.ok:
if request.GET.get("format") == "json":
return JsonResponse({"results": [], "count": 0}, status=resp.status or 500)
messages.error(request, f"Ошибка API ({resp.status})")
return render(
request,
"employees/employees_list.html",
{"employees": [], "q": q, "o": ordering, "page": page},
)
payload = resp.json or {}
items, count, next_url, prev_url = _extract_items(payload)
# Преобразуем API URLs в frontend URLs
next_url = _convert_api_url_to_frontend(next_url, request.path)
prev_url = _convert_api_url_to_frontend(prev_url, request.path)
# JSON-ветка для автокомплита
if request.GET.get("format") == "json":
page = request.GET.get("page") or 1
ordering = request.GET.get("o") or "last_name"
q = (request.GET.get("q") or "").strip()
department = request.GET.get("department")
position = request.GET.get("position")
is_active = request.GET.get("is_active")
params = {"page": page, "ordering": ordering}
if q:
params["search"] = q
if department:
params["department"] = department
if position:
params["position"] = position
if is_active:
params["is_active"] = is_active
resp = api.get("v1/employees/", params=params)
if not resp.ok:
return JsonResponse({"results": [], "count": 0}, status=resp.status or 500)
payload = resp.json or {}
items, count, next_url, prev_url = _extract_items(payload)
# exclude: "1,2,3"
raw_ex = (request.GET.get("exclude") or "").strip()
try:
@ -268,34 +259,18 @@ def employee_list(request):
items = [e for e in items if _id(e) not in ex_ids][:limit]
return JsonResponse({"results": items, "count": len(items)})
# HTML-ветка (как было)
# Проверяем права на создание сотрудников
create_url = None
has_perm = (
request.user.has_perm('employees.add_employee')
or request.user.is_staff
or request.user.is_superuser
)
if has_perm:
from django.urls import reverse
create_url = reverse('employees:employee_create')
# Если фильтрация по отделу, меняем label
count_label = "Всего:"
if department:
count_label = "В отделе:"
# Получаем списки для фильтров
# HTML-ветка - НЕ загружаем данные сотрудников, они загрузятся через JavaScript
# Получаем только справочники для фильтров
departments_list = []
positions_list = []
try:
# Получаем все отделы
# Получаем все отделы для фильтров
dept_resp = api.get("v1/departments/", params={"page_size": 1000})
if dept_resp.ok:
dept_data = dept_resp.json or {}
departments_list = dept_data.get("results", [])
# Получаем все должности
# Получаем все должности для фильтров
pos_resp = api.get("v1/positions/", params={"page_size": 1000})
if pos_resp.ok:
pos_data = pos_resp.json or {}
@ -304,15 +279,7 @@ def employee_list(request):
pass
context = {
"employees": items,
"count": count,
"q": q,
"o": ordering,
"page": page,
"next_url": next_url,
"prev_url": prev_url,
"create_url": create_url,
"count_label": count_label,
"employees": [], # Пустой список - данные загрузятся через JS
"departments_list": departments_list,
"positions_list": positions_list,
}

View File

@ -23,81 +23,11 @@
<div class="card mb-3">
<div id="empList" class="list-view card-list">
{# Список загружается через JavaScript из employeesList.js #}
{% if employees %}
{% for emp in employees %}
<article class="emp-row list-row"
data-name="{{ emp.last_name }} {{ emp.first_name }} {{ emp.patronymic }} {% firstof emp.position.name emp.position_label '' %}"
data-depts="{% if emp.departments %}{% for d in emp.departments %}{% if d.name %}{{ d.name }}{% if not forloop.last %}, {% endif %}{% endif %}{% endfor %}{% elif is_filtered_by_department and current_department %}{{ current_department.name }}{% endif %}">
<header class="card-header">
<div class="card-icon">
{% if emp.avatar %}
<img src="{{ emp.avatar }}" alt="{{ emp.last_name }} {{ emp.first_name }}">
{% else %}
<i class="bi-person"></i>
{% endif %}
</div>
<div class="card-meta flex-grow-1">
<div class="d-flex align-items-center gap-2 flex-wrap">
{% if emp.id %}
<a href="{% url 'employees:employee_detail' emp.id %}" class="card-title text-decoration-none">
{{ emp.last_name }} {{ emp.first_name }}{% if emp.patronymic %} {{ emp.patronymic }}{% endif %}
</a>
{% else %}
<span class="card-title">
{{ emp.last_name }} {{ emp.first_name }}{% if emp.patronymic %} {{ emp.patronymic }}{% endif %}
</span>
{% endif %}
{% if emp.position and emp.position.name %}
<span class="emp-pos">— {{ emp.position.name }}</span>
{% elif emp.position_label %}
<span class="emp-pos">— {{ emp.position_label }}</span>
{% elif emp.position %}
<span class="emp-pos">— Должность #{{ emp.position }}</span>
{% endif %}
</div>
{% if emp.departments %}
<div class="card-subtitle">
{% for d in emp.departments %}
{% if d.name %}
{% if d.id %}
<a href="{% url 'employees:department_detail' d.id %}">{{ d.name }}</a>
{% else %}
{{ d.name }}
{% endif %}
{% if not forloop.last %}, {% endif %}
{% endif %}
{% endfor %}
</div>
{% elif is_filtered_by_department and current_department %}
<div class="card-subtitle">{{ current_department.name }}</div>
{% endif %}
</div>
<div class="ms-auto d-flex align-items-center gap-2">
{% if emp.id and user.id != emp.id %}
{% if can_manage_roles and is_filtered_by_department and current_department and emp.id != current_department.head_id %}
<a href="{% url 'employees:edit_department_role' current_department.id emp.id %}?next={{ request.get_full_path|urlencode }}"
class="btn btn-ghost action-btn" title="Изменить роль">
<i class="bi-pencil"></i><span class="d-none d-sm-inline ms-1">Роль</span>
</a>
{% endif %}
<a href="{% url 'communications:start_private_chat' emp.id %}"
class="btn btn-ghost action-btn" title="Чат">
<i class="bi-chat-dots"></i><span class="d-none d-sm-inline ms-1">Чат</span>
</a>
{% endif %}
</div>
</header>
<div class="card-body"></div>
</article>
{% endfor %}
{% else %}
<div class="alert alert-info shadow-sm">Нет сотрудников.</div>
{% endif %}
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Загрузка...</span>
</div>
</div>
</div>
</div>