В FastReport Online Designer можно добавлять собственные компоненты отчёта — со своей иконкой в палитре, свойствами в панели Properties и собственным окном настройки. Никакой магии: вы пишете плагин на JavaScript, загружаете его в билдер, и он компилируется прямо в сборку дизайнера.
В этой статье мы с нуля сделаем компонент «Рейтинг», который отображает значение в виде звёзд, и пройдём весь путь: от скачивания SDK до готовой сборки дизайнера с плагином и серверной частью.
У компонента две половины:
.frx.Их связывает имя типа: короткое имя .NET-класса, поле type в плагине и XML-тег в .frx должны быть одной и той же строкой.
Половины можно делать по очереди. Дизайнеру .NET-класс не нужен, поэтому сначала соберём и проверим плагин, а серверную часть добавим в конце.
В билдере откройте Сборка → Свои компоненты и нажмите Скачать SDK. В архиве лежат README.md с кратким введением и examples/DemoChartObject — рабочий пример.
Папка плагина одновременно служит рабочей папкой для IDE. Скопируйте examples/DemoChartObject и переименуйте копию в RatingObject: в ней уже всё готово для автодополнения — sdk.d.ts с типами SDK, jsconfig.json и package.json с типами React под версию React дизайнера. Выполните в этой папке npm install и откройте её в IDE.
Плагин — это папка с файлом index.jsx (или index.js). Замените содержимое RatingObject/index.jsx описанием компонента:
import React from 'react';
import {registerComponent, tr} from 'sdk';
import {Input, ColorInput, Button, FormField} from 'sdk/controls';
registerComponent({
apiVersion: 1,
type: 'RatingObject',
core: 'net',
palette: {
title: 'Rating',
info: 'Star rating',
icon: 'icon-chart',
},
defaultValues: {
'Value': 3,
'Maximum': 5,
'StarColor': '#f5a623',
},
size: {width: 160, height: 32},
properties: [
{prop: 'Data:Value', type: 'number', label: 'Value', attrs: {min: 0}},
{prop: 'Data:Maximum', type: 'number', label: 'Maximum', attrs: {min: 1}},
{prop: 'Appearance:StarColor', type: 'color', label: 'Star color'},
],
});
Этого уже достаточно, чтобы компонент появился в палитре и получил свойства:
palette задаёт карточку в палитре. Значок здесь штатный, но можно указать и свой SVG или PNG в виде data:-URI.defaultValues хранит значения по умолчанию. В .frx попадают только значения, отличающиеся от них, — так же работает и FastReport .NET.properties описывает поля панели свойств в формате Категория:Имя. Поля Design:Name и Layout:Left/Top/Width/Height SDK добавит сам.Сохранять и читать .frx вручную не нужно: унаследованная реализация уже умеет работать со всеми объявленными свойствами.
Чтобы компонент выглядел на странице как рейтинг, добавьте в описание метод draw. Рамку, перемещение и изменение размера компонент получает от базового класса Component, поэтому draw отвечает только за содержимое. Рисовать нужно в контейнер this.$content: к моменту вызова он уже создан.
draw: function () {
if (!this.$stars) {
this.$stars = document.createElementNS('http://www.w3.org/2000/svg', 'g');
this.$content.append(this.$stars);
}
const maximum = Number(this.prop('Maximum')) || 1;
const value = Number(this.prop('Value')) || 0;
const color = String(this.prop('StarColor'));
const height = this.prop('Height');
const step = this.prop('Width') / maximum;
const stars = [];
for (let i = 0; i < maximum; i++) {
const fill = i < value ? color : '#d9d9d9';
stars.push(`<text x="${step * (i + 0.5)}" y="${height * 0.75}" text-anchor="middle" font-size="${height * 0.8}" fill="${fill}">★</text>`);
}
this.$stars.innerHTML = stars.join('');
},
Окно настройки открывается двойным кликом по компоненту. Соберём его из штатных контролов sdk/controls, чтобы оно выглядело как встроенные редакторы:
function RatingDialog ({component, popup}) {
const ok = () => {
component.model.submit();
popup.submitAndClose();
component.render();
};
const cancel = () => {
component.model.clear();
popup.close();
};
return (
<div>
<FormField label={`${tr('Value')}:`}>
<Input half type="number" min={0} model={component.model} prop="Value"/>
</FormField>
<FormField label={`${tr('Maximum')}:`}>
<Input half type="number" min={1} model={component.model} prop="Maximum"/>
</FormField>
<FormField label={`${tr('Star color')}:`}>
<ColorInput half model={component.model} prop="StarColor"/>
</FormField>
<div style={{textAlign: 'right'}}>
<Button variant="primary" text="OK" minWidth={60} style={{marginRight: 6}} onClick={ok}/>
<Button minWidth={60} text={tr('Cancel')} onClick={cancel}/>
</div>
</div>
);
}
Контролы пишут правки в component.model. Это черновик: настоящие свойства компонента меняются только после submit(), поэтому кнопка Отмена ничего не портит.
Подключите окно в описании компонента:
editor: (component, popup) => <RatingDialog component={component} popup={popup}/>,
Плагин должен содержать свои переводы. Ключом удобно делать английский текст: если перевода нет, дизайнер покажет сам ключ.
const TRANSLATIONS = {
en: {'Rating': 'Rating', 'Star rating': 'Star rating', 'Value': 'Value', 'Maximum': 'Maximum', 'Star color': 'Star color', 'Cancel': 'Cancel'},
ru: {'Rating': 'Рейтинг', 'Star rating': 'Рейтинг в звёздах', 'Value': 'Значение', 'Maximum': 'Максимум', 'Star color': 'Цвет звёзд', 'Cancel': 'Отмена'},
};
Добавьте в описание компонента строку translations: TRANSLATIONS. Подписи в палитре и панели свойств дизайнер переведёт сам, а в своём коде используйте tr('Value').
RatingObject. Удалять node_modules перед упаковкой не нужно — билдер учтёт только пакеты из dependencies, а devDependencies (в том числе типы React) в сборку не попадут.Откройте дизайнер: в палитре появился Рейтинг. Добавьте его на страницу, поменяйте значение в панели свойств и откройте окно настройки двойным кликом.
Если вы собираете дизайнер через Builder API, загрузите архив запросом POST /builderAPI/extensions и перечислите плагины в запросе сборки: "extensions": ["RatingObject"].
Чтобы сервер мог построить отчёт с новым объектом, нужен .NET-класс с тем же именем в отдельной сборке:
public class RatingObject : ReportComponentBase
{
// Draw, Serialize, Deserialize, Assign
}
public class RatingAssemblyInitializer : AssemblyInitializerBase
{
public RatingAssemblyInitializer()
{
RegisteredObjects.Add(typeof(RatingObject), "ReportPage", myBitmap, "Rating");
}
}
Сборку подключают через секцию <Plugins> в FastReport.config либо прямым вызовом при старте приложения. Готовый образец такого плагина — Extras/Misc/TeeChartObjectPlugin в FastReport .NET.
Пока класс не развёрнут, сервер не сможет прочитать отчёт с этим объектом и выдаст ClassException — в том числе при построении превью. Сборка дизайнера без плагина при этом откроет отчёт и сохранит незнакомый объект с тегом, атрибутами и вложенными XML-элементами.
Плагин умеет больше, чем показано в статье: наследовать поведение штатных компонентов через extends, подключать собственные редакторы свойств через registerPropertyEditor и использовать сторонние библиотеки — их ставят через npm install <пакет> и упаковывают вместе с node_modules. Всё это описано в главе «Свои компоненты» руководства пользователя Online Designer, а пример DemoChartObject показывает эти приёмы в рабочем коде.
Да, для работы в дизайнере достаточно JS-плагина. Но без класса на сервере отчёт с этим компонентом построить не удастся: сервер выдаст ClassException.
Укажите в palette.icon свой SVG или PNG в виде data:-URI. Без него SDK подставит штатный значок.
В корне архива должна быть одна папка плагина, а в ней — файл index.jsx или index.js. Список проблем билдер покажет сразу при загрузке.
Собственный компонент для FastReport Online Designer — это две половины, связанные одинаковым именем типа: JS-плагин для редактора и .NET-класс для сервера. Чтобы собрать всё уже сегодня, скачайте SDK, скопируйте пример DemoChartObject и подставьте свои свойства, превью и окно настройки — дизайнер пересоберётся вместе с плагином. Серверную часть подключайте параллельно: даже до развёртывания класса отчёт с новым компонентом можно открыть и сохранить без потерь.