dynamic table

This commit is contained in:
root
2026-01-10 19:43:04 +03:00
parent 5f5a28e292
commit d27f66953c
9 changed files with 387 additions and 251 deletions
@@ -0,0 +1,54 @@
<tbody>
{% if items is defined and items|length > 0 %}
{% for item in items %}
<tr>
{# Рендерим каждую колонку #}
{% for key, column in columns %}
<td>
{{ render_cell(item, key, column)|raw }}
</td>
{% endfor %}
{# Колонка действий #}
{% if actionsConfig is defined and actionsConfig|length > 0 %}
<td class="actions-cell text-end">
{{ render_actions(item, actionsConfig)|raw }}
</td>
{% endif %}
</tr>
{% endfor %}
{% else %}
{# Пустое состояние #}
<tr>
<td colspan="{{ columns|length + (actions is defined and actions ? 1 : 0) }}"
class="text-center py-5">
{% if emptyIcon is defined and emptyIcon %}
<div class="mb-3">
<i class="{{ emptyIcon }} text-muted" style="font-size: 3rem;"></i>
</div>
{% endif %}
<p class="text-muted mb-3">{{ emptyMessage|default('Нет данных') }}</p>
{% if emptyActionUrl is defined and emptyActionUrl %}
<a href="{{ emptyActionUrl }}" class="btn btn-primary">
{% if emptyActionIcon is defined and emptyActionIcon %}
<i class="{{ emptyActionIcon }} me-2"></i>
{% endif %}
{{ emptyActionLabel|default('Добавить') }}
</a>
{% endif %}
</td>
</tr>
{% endif %}
</tbody>
{# Футер с пагинацией #}
<tfoot>
<tr>
<td colspan="{{ columns|length + 1 }}">
{{ include('@components/table/pagination.twig', {
pagination: pagerDetails,
id: id
}) }}
</td>
</tr>
</tfoot>
+19
View File
@@ -0,0 +1,19 @@
{#
macros.twig - Универсальные макросы для таблиц
Макросы:
- render_actions(actions): Рендерит кнопки действий для строки таблицы
#}
{% macro render_actions(actions) %}
<div class="btn-group btn-group-sm">
{% for action in actions %}
<a href="{{ action.url }}"
class="btn {{ action.class|default('btn-outline-primary') }}"
title="{{ action.title|default('') }}"
{% if action.confirm %}onclick="return confirm('{{ action.confirm }}');"{% endif %}>
{% if action.icon %}<i class="{{ action.icon }}"></i>{% endif %}
</a>
{% endfor %}
</div>
{% endmacro %}
+34 -44
View File
@@ -5,20 +5,25 @@
- id: ID контейнера таблицы (обязательно)
- url: URL для AJAX-загрузки данных (обязательно)
- perPage: Количество записей на странице (по умолчанию 10)
- columns: Ассоциативный массив ['name' => ['label' => 'Name', 'width' => '40%']]
- sort: Текущий столбец сортировки
- order: Направление сортировки
- filters: Текущие значения фильтров
- items: Массив объектов модели (автоматический рендеринг)
- rows: Предварительно построенные строки (устаревший формат, для совместимости)
- columns: Конфигурация колонок
Пример:
columns: {
name: { label: 'Имя', width: '40%' },
email: { label: 'Email' }
}
- items: Массив объектов для отображения
- actionsConfig: Конфигурация действий строки
Пример:
actionsConfig: [
{ label: 'Ред.', url: '/clients/edit/{id}', icon: 'bi bi-pencil', class: 'btn-outline-primary' },
{ label: 'Удалить', url: '/clients/delete/{id}', icon: 'bi bi-trash', class: 'btn-outline-danger' }
]
- emptyMessage: Сообщение при отсутствии данных
- emptyActionUrl: URL для кнопки действия (опционально)
- emptyActionLabel: Текст кнопки действия (опционально)
- emptyIcon: FontAwesome иконка (опционально)
- emptyActionUrl: URL для кнопки действия
- emptyActionLabel: Текст кнопки
- emptyIcon: FontAwesome иконка
- tableClass: Дополнительные классы для таблицы
#}
{% set hasRows = (rows is defined and rows|length > 0) or (items is defined and items|length > 0) %}
<div id="{{ id }}" class="data-table" data-url="{{ url }}" data-per-page="{{ perPage|default(10) }}">
<table class="table table-hover mb-0 {{ tableClass|default('') }}">
{# Заголовок таблицы #}
@@ -32,40 +37,24 @@
{# Тело таблицы #}
<tbody>
{% if hasRows %}
{% if rows is defined and rows|length > 0 %}
{# Старый формат: предварительно построенные строки #}
{% for row in rows %}
<tr>
{% for cell in row.cells %}
<td class="{{ cell.class|default('') }}">{{ cell.content|raw }}</td>
{% endfor %}
{% if row.actions is defined %}
<td class="actions-cell text-end">
{{ row.actions|raw }}
</td>
{% endif %}
</tr>
{% if items is defined and items|length > 0 %}
{% for item in items %}
<tr>
{# Рендерим каждую колонку #}
{% for key, column in columns %}
<td>
{{ render_cell(item, key, column)|raw }}
</td>
{% endfor %}
{% elseif items is defined and items|length > 0 %}
{# Новый формат: автоматический рендеринг из объектов модели #}
{% set columnKeys = columns|keys %}
{% for item in items %}
<tr>
{# Ячейки данных #}
{% for columnKey in columnKeys %}
<td>{{ attribute(item, columnKey)|default('—') }}</td>
{% endfor %}
{# Колонка действий #}
{% if actions is defined and actions %}
<td class="actions-cell text-end">
{% if item.actions is defined %}{{ item.actions|raw }}{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
{% endif %}
{# Колонка действий #}
{% if actionsConfig is defined and actionsConfig|length > 0 %}
<td class="actions-cell text-end">
{{ render_actions(item, actionsConfig)|raw }}
</td>
{% endif %}
</tr>
{% endfor %}
{% else %}
{# Пустое состояние #}
<tr>
@@ -76,7 +65,7 @@
<i class="{{ emptyIcon }} text-muted" style="font-size: 3rem;"></i>
</div>
{% endif %}
<p class="text-muted mb-3">{{ emptyMessage|default('Нет данных для отображения') }}</p>
<p class="text-muted mb-3">{{ emptyMessage|default('Нет данных') }}</p>
{% if emptyActionUrl is defined and emptyActionUrl %}
<a href="{{ emptyActionUrl }}" class="btn btn-primary">
{% if emptyActionIcon is defined and emptyActionIcon %}
@@ -90,6 +79,7 @@
{% endif %}
</tbody>
{# Футер с пагинацией #}
<tfoot>
<tr>
<td colspan="{{ columns|length + 1 }}">