Этап подготовки
После выбора типа виджета «HTML» будут доступны 3 вкладки:
-
HTML
- структура карточки-виджета
- заголовки карточки
- создание контейнеров для графиков и диаграмм.
- CSS Стилизация визуализаций, улучшение их внешнего вида. Определение стилей карточки виджета, цветов, шрифтов и анимаций. Обратите внимание, что в библиотеках для построения графиков(Apache Echarts и другие) настройка внешнего вида обычно производится в JS через передачу свойств стилей в объекте конфигурации(option).
- JS(JavaScript) Динамическое создание и управление визуализациями. Подключение библиотек, таких как D3.js или Chart.js, для создания интерактивных графиков и диаграмм, обновление данных в реальном времени.
В первую очередь необходимо подключить библиотеку Apache Echarts. Это можно сделать на вкладке HTML с помощью тега script. Можно подключить через CDN(Content Delivery Network) – в данном случае библиотека будет загружаться через интернет. Пример,
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js" integrity="sha512-k37wQcV4v2h6jgYf5IUz1MoSKPpDs630XGSmCaCCOXxy2awgAWKHGZWr9nMyGgk3IOxA1NxdkN8r1JHgkUtMoQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Также можно загрузить .js файл с библиотекой напрямую в AW и в src указать название файла, например:
<script src="echarts.min.js"></script>
Также на этом этапе подготовим данные, добавим поля в группы и агрегаты, настроим сортировку и фильтрацию у полей модели.
Осторожнее!
Хотя агрегаты можно посчитать и в JavaScript коде, лучше реализовывать их через встроенный OLAP, чтобы не перегружать клиентский код лишними операциями. При необходимости можно реализовать их и в JS, но нужно следить за производительностью. Также не забывайте, что можно вывести данные модели в виде плоской таблицы, если добавить поля в столбцы без указания функции агрегации.Однако такой подход рискован — если будет большая модель из миллионов строк, памяти браузера может не хватить + любая неосторожная операция может привести к зависанию виджета.
Добавляем диаграмму Echarts
Этап подготовки прошли, теперь можно добавить простой пример диаграммы:
добавим в HTML простую разметку нашей карточки-виджета. Обратите внимание на
<div id="chart"></div>
Этот элемент будем использовать для рендера Apache Echarts диаграммы, зададим ему идентификатор id = “chart”.
Код для вкладки HTML представлен ниже:
<article class="widget-card">
<header>
<h1 class="main-header">Динамика поступлений на склады</h1>
<h2 class="sub-header">с 2018 года</h2>
</header>
<div class="chart-container">
<div id="chart"></div>
</div>
</article>
Добавим стилизацию карточки, для этого на вкладку CSS добавим следующий код:
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
:root {
--main-color: white;
--text-color: black;
}
:root.dark {
--main-color: rgb(43, 43, 43);
--text-color: #d3d6e0;
}
.widget-card {
font-family: 'Montserrat', sans-serif;
border: 1px solid #757784;
border-radius: 24px;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-inline: 37px;
padding-block: 28px;
color: var(--text-color);
background-color: var(--main-color);
}
.main-header {
font-size: 20px;
font-weight: 500;
line-height: 24px;
}
.sub-header {
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
.chart-container {
width: 100%;
height: 100%;
}
#chart {
width: 100%;
height: 100%;
}
На вкладке JavaScript добавим следующие строки:
-
Найдем наш div-контейнер для диаграммы по id:
let chartDom = document.getElementById(‘chart’);
-
Инициализируем объект echarts в этом контейнере. Если библиотеку подключили правильно(см. Этап подготовки), то библиотека будет доступна по имени echarts:
let chart = echarts.init(chartDom);
-
Добавим обработчик события изменения размеров окна. В нем будем вызывать аналогичный метод для изменения размеров Echarts диаграммы. С этим кодом наш график станет "резиновым":
window.addEventListener(‘resize’, () => { chart.resize(); })
-
Объявим функцию render. В ней будем получать данные из модели и передавать настройки объекту диаграммы. Функция render будет вызываться повторно при изменении виджета, например, при установке фильтра на дашборде. Поэтому в теле функции необходимо реализовать обновление объекта настроек. Для начала получим данные из модели, для этого внутри функции напишем
console.log(window.DATA)
и откроем консоль разработчика в браузере(Ctrl + Shift + I). Отметим, что также для дебага также можно использовать ключевое слово debugger и пользоваться всеми возможностями devtools. Данные модели будут доступны в свойстве DATA глобального объекта window. На скриншоте представлен пример соответствия данных из табличного представления и из объекта JavaScript.
-
Для отрисовки Apache Echarts диаграммы необходимо передать объект настроек(option) экземпляру echarts. Cоздадим объект option вне функции render.
Основные свойства, которые понадобятся для настройки:
- grid - настройка сетки диаграммы;
- xAxis, yAxis - настройка осей;
- series - серии данных, на одной диаграмме может быть несколько серий с разными типами;
Подробнее можно узнать в документации библиотеки:
https://echarts.apache.org/en/option.html#title
В нашем примере объект будет иметь следующую структуру:
const option = { title: { text: 'Waterfall Chart', show: false }, tooltip: { show: true, trigger: 'item' }, xAxis: { type: 'category', data: [], axisLabel: { show: true } }, yAxis: {}, series: [ { name: 'Placeholder', type: 'bar', stack: 'Total', itemStyle: { borderColor: 'transparent', color: 'transparent' }, emphasis: { itemStyle: { borderColor: 'transparent', color: 'transparent' } }, data: [], tooltip: { show: false, extraCssText: 'display: none;' }, stackStrategy: 'all' }, { name: 'Итог за месяц', type: 'bar', stack: 'Total', stackStrategy: 'all', data: [], label: { show: true, position: 'outside' }, // tooltip: {}, itemStyle: { color: v => { return v.value > 0 ? '#65cccc' : '#f96c89' } }, } ] };
-
При создании кастомных виджетов основной задачей является правильно передать данные из модели AW в option Apache Echarts. Для этого данные нужно преобразовать к подходящему виду. Они могут быть представлены в свойстве dataset или series.data. Подробное описание также можно посмотреть в документации.
В нашем случае будем создавать waterfall-диаграмму. Для этого необходимо определить 2 серии данных:
- отображаемую часть;
- невидимую часть столбца, placeholder;
Объявим функцию transformData. Аргументом передадим данные из модели и внутри функции получим 3 массива:
- currentCategories - данные для оси oX;
- currentData - данные для отображаемой серии;
- dsum - данные placeholder`а;
function transformData(data) { const currentData = []; const currentCategories = []; let dsum = [0]; let sum = 0; data.forEach(row => { currentData.push(row.calc__income_exprense_diff.agg_value); currentCategories.push(row.calc__year_month.value); sum += row.calc__income_exprense_diff.agg_value; dsum.push(sum); }); return [currentCategories, currentData, dsum]; }
-
И заключительным шагом в функции render вызовем функцию трансформации и установим преобразованные массивы в качестве данных для серий:
option.series[0].data = dsum; option.series[1].data = currentData;
Добавим значения осей:
option.xAxis.data = currentCategories;
И наконец обновим объект диаграммы новой конфигурацией настроек:
option && chart.setOption(option);
Код функции render представлен ниже:
function render() { if (!window.DATA.data.length) return; const [currentCategories, currentData, dsum] = transformData(window.DATA.data); option.series[0].data = dsum; option.series[1].data = currentData; option.xAxis.data = currentCategories; option && chart.setOption(option); }
Итог
Полный код вкладки HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js" integrity="sha512-k37wQcV4v2h6jgYf5IUz1MoSKPpDs630XGSmCaCCOXxy2awgAWKHGZWr9nMyGgk3IOxA1NxdkN8r1JHgkUtMoQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<article class="widget-card">
<header>
<h1 class="main-header">Динамика поступлений на склады</h1>
<h2 class="sub-header">с 2018 года</h2>
</header>
<div class="chart-container">
<div id="chart"></div>
</div>
</article>
Полный код вкладки CSS
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
:root {
--main-color: white;
--text-color: black;
}
:root.dark {
--main-color: rgb(43, 43, 43);
--text-color: #d3d6e0;
}
.widget-card {
font-family: 'Montserrat', sans-serif;
border: 1px solid #757784;
border-radius: 24px;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-inline: 37px;
padding-block: 28px;
color: var(--text-color);
background-color: var(--main-color);
}
.main-header {
font-size: 20px;
font-weight: 500;
line-height: 24px;
}
.sub-header {
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
.chart-container {
width: 100%;
height: 100%;
}
#chart {
width: 100%;
height: 100%;
}
Полный код вкладки JS
// ECHARTS INIT
let chartDom = document.getElementById('chart');
let chart = echarts.init(chartDom);
window.addEventListener('resize', () => {
chart.resize();
})
const option = {
title: {
text: 'Waterfall Chart',
show: false
},
tooltip: {
show: true,
trigger: 'item'
},
xAxis: { type: 'category', data: [], axisLabel: { show: true } },
yAxis: {},
series: [
{
name: 'Placeholder',
type: 'bar',
stack: 'Total',
itemStyle: {
borderColor: 'transparent',
color: 'transparent'
},
emphasis: {
itemStyle: {
borderColor: 'transparent',
color: 'transparent'
}
},
data: [],
tooltip: {
show: false,
extraCssText: 'display: none;'
},
stackStrategy: 'all'
},
{
name: 'Итог за месяц',
type: 'bar',
stack: 'Total',
stackStrategy: 'all',
data: [],
label: {
show: true,
position: 'outside'
},
// tooltip: {},
itemStyle: { color: v => { return v.value > 0 ? '#65cccc' : '#f96c89' } },
}
]
};
function transformData(data) {
const currentData = [];
const currentCategories = [];
let dsum = [0];
let sum = 0;
data.forEach(row => {
currentData.push(row.calc__income_exprense_diff.agg_value);
currentCategories.push(row.calc__year_month.value);
sum += row.calc__income_exprense_diff.agg_value;
dsum.push(sum);
});
return [currentCategories, currentData, dsum];
}
function render() {
if (!window.DATA.data.length) return;
const [currentCategories, currentData, dsum] = transformData(window.DATA.data);
option.series[0].data = dsum;
option.series[1].data = currentData;
option.xAxis.data = currentCategories;
option && chart.setOption(option);
}
В результате получили кастомную waterfall-диаграмму. Общие принципы из данного примера можно использовать для создания других типов визуализаций.
Примеры доступны для демо-пользователей https://aw-demo.ru/, запись прямого эфира можно посмотреть по ссылке:
https://www.youtube.com/watch?v=qRS1QGojVUc&t=1s