Директива qTableExport
Директива qTableExport позволяет выгрузить данные из таблицы в формате excel.
С чего начать
Установите библиотеку
npm install @diasoft/qpalette-table-addonsИмпортируйте модуль дрективы:
import { QTableExportModule } from '@diasoft/qpalette-table-addons';
imports: [
...
QTableExportModule,
...
]Встройте директиву в тег таблицы и укажите в ней API, в которой хранятся данные для таблицы:
Использование
Для скачивания данных существует два способа:
-
Горячие клавиши Alt + P
-
По кнопке
Реализация данного способа зависит от выбранной настройки скачивания данных, которые рассмотренны в разделеНастройка -> Скачивание по кнопке
Настройка
Фильтры
Чтобы, при выгрузке данных, учитывались значения фильтров из шапки таблицы, сортировка и другие настройки, необходимо их передать через свойство директивыqteSearchParams. Они совместно с переданной API будут использованы в качестве параметров для запроса данных.
ДляqteSearchParamsнеобходимо передать объект, где ключ - наименование фильтра таблицы, значение - подготовленные http-параметры.
export class customersTable {
filters = {
name: 'ci(contains(тест))',
systemName: 'ci(contains(qd))',
sort: 'changeDate,desc',
page: 0
}
}Максимальное количество
Для установки максимального количества данных, которое может экспортировать пользователь, необходимо передать выбранное количество свойствуqteMaxTotalRecordsForExport. По умолчанию данное значение равно 500.
Необходимо отметить, что при увеличении данного параметра, нагрузка на приложение находится в ответственности разработчика.
Выбор стобцов
Для выбора выгружаемых столбцов, необходимо передать массив с их наименованиями свойствуqteCurrentCols.
{{ colsNamesExample }}Поскольку лежащая в основе библиотека не принимает вложенные данные, то директива преобразует их вJSON.
Кастомизация
Самотоятельно подготовленные или кастомизированные данные готовые к экспорту необходимо передать свойствуqteCurrentCols. Формат данных должен представлять массив простых объектов, где ключ - это наименование столбца, а значение является простым типом.
{{ colsDataExample }}Экспорт по кнопке
Для скачивания по кнопке, необходимо использовать метод директивы export(). Если вы передаёте катомизированные данные, то они сразу экспортируются, без показа диалогового окна.
{{ exportButtonTemplateExample }}Если вы передаёте катомизированные данные через свойствоqteCurrentCols, то они сразу экспортируются, без показа диалогового окна.
Пример
<p-card
styleClass="projects-card p-card--no-padding p-card--no-border p-mb-0"
*ngIf="{
table: (tableData$ | async),
loading: (isLoading$ | async)
} as data"
>
<ng-template pTemplate="header">
<div class="q-header p-ai-center p-mb-0 p-p-3">
<h1 class="q-header__title p-pl-0">Проекты</h1>
</div>
<div class="q-header__filter-wrapper">
<form class="projects__filters q-flex q-flex--gap-1" [formGroup]="filtersForm">
<q-filter-wrapper label="Наименование:">
<input pInputText type="text" formControlName="name"/>
</q-filter-wrapper>
<q-filter-wrapper label="Автор:">
<input pInputText type="text" formControlName="author"/>
</q-filter-wrapper>
</form>
<div class="projects__actions">
<button
pButton
class="p-button-text p-button-secondary p-button-icon-only p-button-tiny"
icon="pi pi-cloud-upload"
pTooltip="Выгрузить"
tooltipPosition="left"
(click)="download()"
></button>
</div>
</div>
</ng-template>
<div class="full-view">
<div class="full-view__container">
<div class="table-wrapper">
<p-table
#table
qTableExport="{ { api } }"
[customFilters]="filtersForm.valueChanges | async"
responsiveLayout="scroll"
styleClass="p-datatable-unstriped"
scrollHeight="flex"
[lazy]="true"
[loading]="data.loading ?? false"
[value]="data.table?.data ?? []"
[rows]="rows"
[rowsPerPageOptions]="[5, 10, 30, 50]"
[scrollable]="true"
[columns]="columns"
[paginator]="true"
[totalRecords]="data.table?.totalItems ?? 0"
(onLazyLoad)="loadData($event)"
>
<ng-template pTemplate="header">
<tr>
<th [pSortableColumn]="column.field" *ngFor="let column of columns">
<span>{ { column.header } }</span>
<p-sortIcon *ngIf="column.field !== 'systemName'" [field]="column.field"
[style.margin-left]="'.5rem'"></p-sortIcon>
<p-columnFilter *ngIf="column.field === 'systemName'" type="text" field="systemName" display="menu"
[showOperator]="false" [showAddButton]="false"
[style.margin-left]="'.5rem'"></p-columnFilter>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-project>
<tr class="table-body">
<td *ngFor="let column of columns">
<span *ngIf="column.field !== 'changeDate'">{ { project[column.field] ?? "" } }</span>
</td>
</tr>
</ng-template>
<ng-template pTemplate="paginatorleft">
<div class="flex align-items-center justify-content-between">
Итого: { { data.table?.totalItems ?? 0 } } записей.
</div>
</ng-template>
</p-table>
</div>
</div>
</div>
</p-card>Пример данных API
Структура данных
interface EmployeeData {
id: number;
name: string;
email: string;
createdAt: string; // ISO дата
updatedAt: string; // ISO дата
status: number; // код статуса
managerId: number;
managerIdExpand: {
// expand данные
fullName: string;
position: string;
};
salary: number;
tags: Array<{
// массив тегов
id: number;
name: string;
color: string;
}>;
rating: number;
}Пример данных
const rowsData: EmployeeData[] = [
{
id: 1,
name: "Иван Петров",
email: "ivan@example.com",
createdAt: "2024-01-15T14:30:00Z",
updatedAt: "2024-01-20T10:15:00Z",
status: 1,
managerId: 101,
managerIdExpand: {
fullName: "Мария Сидорова",
position: "Руководитель отдела",
},
salary: 150000,
tags: [
{ id: 1, name: "VIP", color: "gold" },
{ id: 2, name: "Постоянный", color: "blue" },
],
rating: 4.5,
},
];Конфигурация таблицы
Основные параметры
const config: ICustomTableConfig = {
tableId: "employees", // Уникальный ID
exportFileName: "Сотрудники", // Название файла экспорта
isExport: true, // Включить экспорт
columnsWithFilter: [
/* колонки */
],
};Типы колонок и их форматирование
| Тип | Форматирование | Пример в экспорте |
|---|---|---|
COMMON | Текст как есть | "Иван Петров" |
COMMON + options | Справочник | 1 → "Активный" |
COMMON + expandField | Вложенные данные | managerIdExpand.fullName |
DATE | Только дата | "15.01.2024" |
DATE_TIME | Дата и время | "15.01.2024 14:30:00" |
NUMBER | Число с разделителями | "150 000 RUB" |
TAGS | Список через запятую | "VIP, Постоянный" |
FUNCTION | Кастомное форматирование | "4.5/5 (90%)" |
Пример конфигурации колонок
columnsWithFilter: [
{
key: "id",
label: "ID",
column: {
type: ECustomTableColumnType.COMMON,
hidden: true, // Скрыт в UI, но есть в экспорте
},
},
{
key: "name",
label: "ФИО сотрудника",
column: {
type: ECustomTableColumnType.COMMON,
hidden: false,
},
},
{
key: "createdAt",
label: "Дата создания",
column: {
type: ECustomTableColumnType.DATE_TIME,
hidden: false,
},
},
{
key: "status",
label: "Статус",
column: { type: ECustomTableColumnType.COMMON },
options: [
{ value: 1, label: "Активный" },
{ value: 2, label: "В отпуске" },
],
},
{
key: "managerId",
label: "Руководитель",
column: {
type: ECustomTableColumnType.COMMON,
expandField: "fullName", // Из managerIdExpand.fullName
},
},
];🚀 Метод export()
export() {
// 1. Получаем видимые колонки
const shownColumns = this.columnsListboxModel;
// 2. Преобразуем каждую строку
const rowsForExport = this.rowsData.map((item: any) => {
const object: Record<string, any> = {};
// 3. Обрабатываем каждую колонку
shownColumns.forEach((colConfig) => {
const key = colConfig.key;
const label = colConfig.label;
const columnType = colConfig.column.type;
// 4. Форматируем по типу
switch (columnType) {
case ECustomTableColumnType.COMMON:
if (colConfig.options) {
// Справочники: код → текст
const option = colConfig.options.find(opt => opt.value === item[key]);
object[label] = option ? option.label : item[key];
} else if (colConfig.column.expandField) {
// Expand поля: вложенные данные
const expandField = colConfig.column.expandField;
object[label] = item[key + 'Expand']?.[expandField] || '';
} else {
// Обычное поле
object[label] = item[key];
}
break;
case ECustomTableColumnType.DATE:
// Формат: DD.MM.YYYY
object[label] = item[key] ? moment(item[key]).format('DD.MM.YYYY') : '';
break;
case ECustomTableColumnType.DATE_TIME:
// Формат: DD.MM.YYYY HH:mm:ss
object[label] = item[key] ? moment(item[key]).format('DD.MM.YYYY HH:mm:ss') : '';
break;
case ECustomTableColumnType.NUMBER:
// Число с разделителями + валюта(или любое ваше значение)
object[label] = this.qNumberFormatter.transform(item[key]) + ' RUB';
break;
case ECustomTableColumnType.TAGS:
// Теги через запятую
if (Array.isArray(item[key])) {
object[label] = item[key].map(tag => tag.name).join(', ');
}
break;
case ECustomTableColumnType.FUNCTION:
// Кастомное форматирование
object[label] = colConfig.column?.function(item);
break;
default:
object[label] = item[key];
}
});
return object;
});
// 5. Формируем имя файла
this.table.qteFileName = `${this.config.exportFileName} ${moment().format('DD.MM.YYYY HH:mm:ss')}`; //или как-то по своему
// 6. Экспортируем
this.table.qteCurrentCols = rowsForExport;
this.table.export();
}Результат экспорта
{
"name": "Иван Петров",
"createdAt": "2024-01-15T14:30:00Z",
"status": 1,
"managerId": 101,
"salary": 150000
}После экспорта:
ФИО сотрудника,Дата создания,Статус,Руководитель,Зарплата
"Иван Петров","15.01.2024 14:30:00","Активный","Мария Сидорова","150 000 RUB"Быстрый старт
- Добавьте русские
labelдля всех колонок - Укажите правильный тип для дат (
DATEилиDATE_TIME) - Используйте
optionsдля справочников - Настройте
expandFieldдля вложенных данных
Частые вопросы
Q: Почему в экспорте английские заголовки?
A: Проверьте свойство label в конфигурации колонки.
Q: Дата не форматируется?
A: Убедитесь, что тип колонки DATE или DATE_TIME, а не COMMON.
Q: Expand поля не работают?
A: Проверьте, что в данных есть поле [key]Expand (например, managerIdExpand).