Используйте NocoBase для создания связанной оперативной информационной панели.

В этой статье в качестве примера используется панель управления «системы заказов на работу», чтобы показать, как использовать блок диаграммы NocoBase, блок фильтров и блок JS в сочетании для создания информационной панели данных, которая поддерживает связывание фильтров, детализацию диаграммы и пользовательские стили.

Хотя примеры взяты из сценариев заказов на работу, эти методы также применимы к таким бизнес-системам, как CRM, эксплуатация оборудования, управление проектами, поток утверждений, успех клиентов и т. д.

Tip

В этой статье речь идет не о том, «как использовать блоки JS для написания большого экрана», а о том, как объединить возможности собственных блоков NocoBase и блоков JS: пусть собственные блоки отвечают за стандартные возможности, а блоки JS дополняют персонализированный опыт.

цель сцены

Мы надеемся создать панель управления операциями, которая поможет команде эксплуатации или обслуживания быстро определить текущую рабочую нагрузку:

  • Сколько открытых заказов на работу сейчас?
  • Какие рабочие задания подвержены риску в соответствии с соглашением об уровне обслуживания?
  • Какова тенденция новых заказов на работу?
  • Каков статус и приоритетность заказов на выполнение работ?
  • Нажав на график, вы можете просмотреть соответствующие детали.

Страницу можно условно разделить на четыре слоя:

  1. Верхняя область фильтра: время, группа услуг, тип запроса, приоритет, статус SLA.
  2. Область статистики KPI: открытый журнал невыполненных задач, неназначенные, предупреждение SLA и т. д.
  3. Область анализа диаграммы: тренд, статус, SLA, приоритетное распределение.
  4. Область детализации: щелкните диаграмму, чтобы отобразить соответствующие записи.

Сначала проясним идею конструкции

Когда многие люди создают информационные панели данных, они склонны рассматривать проблему как один из двух вариантов:

Либо используйте все собственные блоки NocoBase, которые легко настроить, но беспокойтесь о том, что стиль и взаимодействие недостаточно гибки; или просто напишите большой блок JS и самостоятельно управляйте запросами, диаграммами, фильтрацией и детализацией, но при этом потеряется удобство, обеспечиваемое конфигурацией с низким кодом.

На самом деле, более рекомендуемый способ — объединить эти два способа.

В этой информационной панели Operations мы не записывали всю страницу в виде большого JS-экрана, а разделили ее по обязанностям:

  • Верхняя фильтрация использует фильтрующий блок, входящий в комплект системы NocoBase;
  • Диаграммы трендов, распределение статусов и распределение SLA используют собственные блоки диаграмм;
  • Карты KPI и подробные сведения используют блоки JS;
  • Блоки фильтров влияют как на блоки диаграмм, так и на блоки JS;
  • После щелчка по диаграмме условия детализации передаются в блок подробностей JS ниже.

Преимущество этого заключается в том, что стандартная статистика и фильтрация по-прежнему сохраняют возможности настройки NocoBase, а персонализированное отображение и сложные взаимодействия дополняются блоками JS. Страница не является ни «только настраиваемой», ни «весь кодом», но конфигурация и код выполняют свои собственные функции.


1. Как настроить стиль блока диаграммы

Блок диаграмм NocoBase может сначала использовать построитель запросов для определения статистического калибра, а затем использовать пользовательскую опцию ECharts для настройки стиля.

Взяв в качестве примера «статистику статуса рабочего задания», построитель запросов можно настроить следующим образом:

  • Технический паспорт: билеты
  • Метрики: количество идентификаторов, псевдоним TicketCount.
  • Размеры: статус

Ключевым моментом является то, что при настройке стиля вам не нужно переписывать запрос, вам нужно только обработать отображение диаграммы на основе ctx.data.objects.

const rows = Array.isArray(ctx.data?.objects) ? ctx.data.objects : [];

Эта строка кода считывает результаты запроса диаграммы. Затем определите метки состояния и цвета:

const labels = {
  new: ctx.t('New'),
  open: ctx.t('Open'),
  pending_customer: ctx.t('Pending customer'),
  resolved: ctx.t('Resolved'),
  closed: ctx.t('Closed'),
};

const colors = {
  new: '#1677ff',
  open: '#22a06b',
  pending_customer: '#f59f00',
  resolved: '#13c2c2',
  closed: '#8c8c8c',
};

Рекомендуется, чтобы весь видимый копирайтинг использовал ctx.t(), чтобы облегчить последующую многоязычную поддержку.

При создании данных диаграммы вы можете прикрепить подробную информацию к каждой точке данных диаграммы:

const data = rows.map((row) => ({
  value: Number(row.ticketCount || 0),
  itemStyle: {
    color: colors[row.status] || '#8c8c8c',
    borderRadius: [6, 6, 0, 0],
  },
  ticketingDrilldown: {
    label: ctx.t('Status') + ': ' + (labels[row.status] || row.status),
    filter: { status: { $eq: row.status } },
  },
}));

Самое критичное здесь — ticketingDrilldown. Это не стандартное поле ECharts, а бизнес-контекст, который мы закладываем сами и который будет использоваться при нажатии на диаграмму позже.

Наконец, вернитесь к опции ECharts:

return {
  grid: { top: 28, right: 22, bottom: 48, left: 42 },
  tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
  xAxis: {
    type: 'category',
    data: rows.map((row) => labels[row.status] || row.status),
  },
  yAxis: {
    type: 'value',
    minInterval: 1,
  },
  series: [
    {
      name: ctx.t('Tickets'),
      type: 'bar',
      barWidth: 36,
      data,
    },
  ],
};

Основная идея этой части:

  • Конструктор запросов отвечает за статистику;
  • Пользовательская опция отвечает за визуальное выражение;
  • Пользовательские поля отвечают за передачу контекста детализации.

2. Пусть блок системного фильтра станет областью наблюдения всей страницы.

Область фильтров в оперативной панели не должна представлять собой изолированную форму. Он представляет текущий диаметр наблюдения всей страницы.

Например, если пользователь выбирает группу услуг, тип запроса и время создания, то ключевые показатели эффективности, диаграммы тенденций, распределение статусов и подробные сведения должны отображаться на основе одного и того же набора условий. В противном случае числа в разных блоках на странице будут бороться друг с другом, и пользователям будет сложно судить, какие данные являются результатом в текущем диапазоне.

Здесь мы напрямую используем блок фильтрации, поставляемый в комплекте с системой NocoBase, вместо того, чтобы писать компонент фильтрации самостоятельно. Собственные блоки фильтров можно естественным образом привязать к блокам диаграммы, что позволяет блоку диаграммы продолжать использовать построитель запросов, разрешения, механизмы обновления и фильтрации.

Top Dashboard scope может настроить следующие элементы фильтра:

  • Created at
  • Service group
  • Request type
  • Priority
  • SLA status

Для блоков JS вам нужно всего лишь прочитать один и тот же набор условий фильтра в коде, а затем преобразовать их в фильтры запроса. Таким образом, ключевые показатели эффективности и подробные сведения также могут соответствовать исходной диаграмме.

Комбинацию условий фильтра можно инкапсулировать в небольшую функцию:

function combineFilters(...filters) {
  const parts = filters.filter(Boolean);
  if (!parts.length) return undefined;
  if (parts.length === 1) return parts[0];
  return { $and: parts };
}

Посчитать по фильтру:

async function countTickets(filter) {
  const resource = ctx.makeResource('MultiRecordResource');
  resource.setResourceName('tickets');
  resource.setPageSize(1);

  if (filter) {
    resource.setFilter(filter);
  }

  await resource.refresh();

  const meta = resource.getMeta?.() || {};
  return Number(meta.count || meta.total || 0);
}

Ключевыми моментами здесь являются:

resource.setFilter(filter);
await resource.refresh();

Блок JS запрашивает бизнес-данные через ресурсы вместо прямого написания SQL. Это упрощает обеспечение согласованности разрешений NocoBase, источников данных и времени выполнения страниц.


3. Используйте блоки JS для отображения карточек KPI.

Ключевые показатели эффективности лучше подходят для использования блоков JS. Потому что KPI обычно представляет собой не один запрос, а комбинацию нескольких бизнес-уровней: незавершенный, неназначенный, предупреждение SLA, нарушение SLA, новое, решенное и т. д.

Блок JS может запрашивать данные на основе текущего диапазона фильтрации и отображать их в статистическую карту.

const { Card, Col, Row, Statistic, Tag } = ctx.libs.antd;

const scopeFilter = getDashboardScopeFilter();

const openBacklog = await countTickets(
  combineFilters(scopeFilter, {
    status: { $notIn: ['resolved', 'closed', 'cancelled'] },
  }),
);

ctx.render(
  <Row gutter={[12, 12]}>
    <Col span={6}>
      <Card size="small">
        <Tag color="blue">{ctx.t('Active')}</Tag>
        <Statistic title={ctx.t('Open backlog')} value={openBacklog} />
      </Card>
    </Col>
  </Row>,
);

Ключевые моменты JS-блоков:

  • Используйте ctx.makeResource() для запроса данных;
  • Используйте ctx.libs.antd для рендеринга интерфейса;
  • Используйте ctx.render() для вывода контента;
  • Перерисуйте фрагменты JS после фильтрации изменений.

На реальной странице кнопка фильтра и кнопка сброса могут настроить поток событий так, чтобы они одновременно обновляли блок JS KPI и блок JS детализации после завершения действия собственного фильтра. Таким образом, пользователь щелкает один раз для фильтрации, и диаграммы и пользовательский контент будут обновляться на основе одного и того же диапазона.


4. JS-блок привязки диаграммы для детализации

Нажатие на диаграмму для ее детализации — очень практичное взаимодействие на информационной панели.

В сценарии заказа на работу пользователь щелкает столбец «Статус: Открыто», и все открытые заказы на работу отображаются в области сведений ниже; Когда пользователь нажимает «Соглашение об уровне обслуживания нарушено», ниже отображаются все заказы на сверхурочную работу.

Идея реализации такова:

  1. Точки данных диаграммы содержат ticketingDrilldown;
  2. Событие диаграммы считывает эту подробную информацию;
  3. Запишите детализированную информацию в контекст целевого блока JS;
  4. Запустите целевой блок JS для повторной визуализации.

Код ключа в событии диаграммы следующий. Сначала найдите блок JS детализации:

const DRILLDOWN_TARGET_UID = 'v7mioopm6rm';

function getDrilldownTarget() {
  if (typeof ctx.getModel === 'function') {
    return ctx.getModel(DRILLDOWN_TARGET_UID);
  }

  const engine =
    ctx.model?.flowEngine || ctx.model?.context?.flowEngine || ctx.engine;
  return engine?.getModel?.(DRILLDOWN_TARGET_UID);
}

Затем запишите условия детализации, полученные при нажатии на диаграмму, в целевой блок:

function applyDrilldown(drilldown) {
  if (!drilldown?.filter) return;

  const target = getDrilldownTarget();
  if (!target?.context?.defineProperty) return;

  target.context.defineProperty('ticketingDashboardDrilldown', {
    value: drilldown,
  });

  target.rerender?.();
}

Наиболее важными являются эти две строки:

target.context.defineProperty('ticketingDashboardDrilldown', {
  value: drilldown,
});
target.rerender?.();

Первая строка передает условие детализации в блок JS, а вторая строка запускает обновление блока JS.

Наконец, привяжите событие клика по диаграмме:

const clickHandler = (params) => {
  applyDrilldown(params?.data?.ticketingDrilldown);
};

chart.on('click', clickHandler);

return () => chart.off('click', clickHandler);

Здесь рекомендуется вернуть очистку:

return () => chart.off('click', clickHandler);

Таким образом, при перенастройке или повторной визуализации диаграммы старые события можно очистить, чтобы избежать повторной привязки.

Приведенный выше код, связанный с событием клика, применим к версиям v2.2.0-beta.10 и более поздним. Ссылка на код старой версии:

chart.off('click');
chart.on('click', clickHandler);

5. Как отображать детали в детализированных блоках JS

Разверните блок JS, чтобы прочитать только что написанный ticketingDashboardDrilldown, а затем запросите данные в соответствии с фильтром в нем.

const drilldown = ctx.model?.context?.ticketingDashboardDrilldown;

if (!drilldown) {
  ctx.render(
    <Alert
      type="info"
      showIcon
      message={ctx.t('Select a chart segment to inspect matching tickets')}
    />,
  );
  return;
}

Если пользователь не щелкнул диаграмму, отобразите подсказку. После нажатия запросите рабочее задание на основе drilldown.filter:

const resource = ctx.makeResource('MultiRecordResource');
resource.setResourceName('tickets');
resource.setFilter(drilldown.filter);
resource.setPageSize(10);
await resource.refresh();

const rows = resource.getData?.() || [];

Затем отобразите таблицу:

const { Table, Typography } = ctx.libs.antd;

ctx.render(
  <>
    <Typography.Title level={5}>
      {ctx.t('Drilldown')}: {drilldown.label}
    </Typography.Title>

    <Table
      size="small"
      rowKey="id"
      dataSource={rows}
      pagination={false}
      columns={[
        { title: ctx.t('Ticket No'), dataIndex: 'ticketNo' },
        { title: ctx.t('Title'), dataIndex: 'title' },
        { title: ctx.t('Status'), dataIndex: 'status' },
        { title: ctx.t('Priority'), dataIndex: 'priority' },
      ]}
    />
  </>,
);

Если вам необходимо очистить условия детализации, вы можете обратиться к

function clearChartDrilldown() {
  if (ctx.model?.context?.defineProperty) {
    ctx.model.context.defineProperty('ticketingDashboardDrilldown', {
      value: null,
    });
  }
  if (typeof ctx.model?.rerender === 'function') {
    ctx.model.rerender();
  }
}

Ключевыми моментами в этой части являются:

  • Диаграмма отвечает только за прохождение фильтра;
  • Блок JS отвечает за запрос и отображение подробностей;
  • Нажимайте на разные диаграммы, чтобы использовать один и тот же блок детализации.

Практические предложения

1. Не спешите кодировать сложную страницу целиком

Самый важный урок из этой страницы: не противопоставляйте нативные возможности возможностям JS.

Если какая-либо возможность уже является встроенной возможностью NocoBase, например фильтрация, запрос диаграммы, отображение таблицы и контроль разрешений, сначала будет использоваться собственный блок. Таким образом, когда поля, условия фильтрации и калибр диаграммы впоследствии настраиваются, их все равно можно настроить в интерфейсе.

Блоки JS больше подходят для обработки частей, с которыми собственные блоки не справляются, например, для объединения нескольких индикаторов в набор ключевых показателей эффективности, специальных стилей карточек, отображения набора пользовательских данных после щелчка по диаграмме или передачи бизнес-контекста между различными блоками.

Другими словами, нативный блок отвечает за «настраиваемые стандартные возможности», а блок JS — за «бизнес-ориентированный персонализированный опыт». Это также наиболее многоразовая идея конструкции этой приборной панели.

2. Для простой статистики сначала используйте построитель запросов блока диаграмм.

При этом сохраняются стандартные запросы NocoBase, разрешения, возможности фильтрации и обновления. Только если стиль диаграммы по умолчанию не может выразить бизнес-направленность, используйте настраиваемую опцию ECharts для визуальной оптимизации.

3. Карты KPI отдают приоритет использованию JS-блоков

Ключевые показатели эффективности часто требуют нескольких запросов, комбинаций условий и пользовательских макетов, а блоки JS более гибкие. Особенно когда ключевые показатели эффективности должны реагировать на один и тот же набор условий системного фильтра, будет яснее использовать блоки JS для их единообразной обработки.

4. События диаграммы должны возвращать очистку.

Рекомендуемый метод письма:

const handler = (params) => {
  // handle click
};

chart.on('click', handler);

return () => chart.off('click', handler);

Не используйте chart.off('click') напрямую для очистки всех событий щелчков, так как это может случайно удалить блок диаграммы или настроить собственный мониторинг панели.


Позвольте ИИ помочь вам его построить

Этот тип информационной панели очень подходит для создания данных с помощью искусственного интеллекта, поскольку он одновременно включает в себя модели данных, статистические калибры, стили диаграмм и взаимодействие со страницами. Вы можете передать ему содержание этой статьи и задать вопросы, используя слова-подсказки ниже.

Вы можете задавать такие вопросы:

Я использую NocoBase для создания оперативной панели управления системой заказов на работу.
Пожалуйста, возьмите сценарий заказа на работу в качестве примера и помогите мне разработать панель управления операциями.

Таблица данных билетов содержит:
ticketNo、title、status、priority、slaStatus、
requestType、serviceGroup、assignee、createdAt、updatedAt。

На странице требуется:

1. Верхний фильтр: Создано в, Группа услуг, Тип запроса, Приоритет, Статус SLA.
2. Карточки KPI: Открытый журнал невыполненных задач, Неназначенные, Предупреждение об уровне обслуживания, Нарушение соглашения об уровне обслуживания, Новые заявки, Решенные заявки.
3. Диаграмма: тенденция созданных заявок, статус заявки, статус SLA, сочетание приоритетов.
4. После щелчка по диаграмме в блоке JS ниже отображается соответствующая таблица детализации билетов.
5. Стиль диаграммы должен подходить для действующего рынка, иметь четкие цвета и компактную структуру.
6. Используйте ctx.t() для всех копий JS.
7. События диаграммы используютchart.on и возвращают функцию очистки.
8. Расставьте приоритеты, используя собственные блоки фильтров и блоки диаграмм NocoBase. Используйте блоки JS только для ключевых показателей эффективности, детализации, специальных стилей и взаимодействия между блоками. Не записывайте всю страницу как один большой блок JS.

Пожалуйста, дайте идеи конфигурации для каждого блока и отметьте ключевой JS-код.

Если у вас уже есть страница, вы также можете позволить ИИ помочь вам ее оптимизировать:

Это мой текущий дизайн панели управления NocoBase:
Вверху находится область фильтра, посередине — 4 диаграммы, а внизу — блок JS детализации.
Пожалуйста, помогите мне оптимизировать с точки зрения опыта оператора:

1. Какие показатели должен отображать KPI?
2. Есть ли необходимость в увязке графиков;
3. Какие столбцы следует отображать в подробностях детализации;
4. Как следует организовывать мероприятия JS-блоков и диаграмм;
5. Какой код разместить в пользовательском варианте диаграммы, а какой — в JS-блоке.

Таким образом, контент, создаваемый ИИ, будет ближе к реальному бизнесу, а не просто к изолированному коду.

Warning

Если вы решите позволить ИИ помочь вам в его создании, воспользуйтесь менеджером резервного копирования для резервного копирования проекта перед его запуском.

Справочная документация