Библиотеки
TableAddons
qTableExport

Директива qTableExport

Директива qTableExport позволяет выгрузить данные из таблицы в формате excel.

С чего начать

Установите библиотеку

npm install @diasoft/qpalette-table-addons

Импортируйте модуль дрективы:

import { QTableExportModule } from '@diasoft/qpalette-table-addons';
 
imports: [
  ...
  QTableExportModule,
  ...
]

Встройте директиву в тег таблицы и укажите в ней API, в которой хранятся данные для таблицы:

Использование

Для скачивания данных существует два способа:

  1. Горячие клавиши Alt + P

  2. По кнопке
    Реализация данного способа зависит от выбранной настройки скачивания данных, которые рассмотренны в разделе Настройка -> Скачивание по кнопке

Настройка

Фильтры

Чтобы, при выгрузке данных, учитывались значения фильтров из шапки таблицы, сортировка и другие настройки, необходимо их передать через свойство директивы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"

Быстрый старт

  1. Добавьте русские label для всех колонок
  2. Укажите правильный тип для дат (DATE или DATE_TIME)
  3. Используйте options для справочников
  4. Настройте expandField для вложенных данных

Частые вопросы

Q: Почему в экспорте английские заголовки? A: Проверьте свойство label в конфигурации колонки.

Q: Дата не форматируется? A: Убедитесь, что тип колонки DATE или DATE_TIME, а не COMMON.

Q: Expand поля не работают? A: Проверьте, что в данных есть поле [key]Expand (например, managerIdExpand).