@ecosystem/ui-core (0.4.0)

Published 2026-08-06 13:11:24 +03:00 by qua8ion

Installation

@ecosystem:registry=
npm install @ecosystem/ui-core@0.4.0
"@ecosystem/ui-core": "0.4.0"

About this package

@ecosystem/ui-core

Общая Vue-библиотека generic entity-UI платформы Ecosystem: готовые страницы «список» и «карточка» поверх entity-API бэкенда (Ecosystem.Core.Web.Entities: /api/entities, /api/viewconfig/{entity}/*, /api/listview/{entity}, /api/crud/{entity}). Выросла из демо-SPA Resources/ (удалено, история — в git).

Форма поставки: пакет-источник

Пакет не собирается самexports указывает на src/index.ts, компиляцию .ts/.vue выполняет Vite-сборка приложения-потребителя (штатный подход для внутренних библиотек). Проверка типов пакета: npm run type-check (vue-tsc).

Подключение (из SPA в этом репозитории):

// package.json приложения
"dependencies": {
  "@ecosystem/ui-core": "file:../packages/ui-core"
}

Настройка HTTP (обязательно, один раз при старте приложения)

Библиотека не знает, как приложение аутентифицируется, — настройте клиент:

import { configureEntityHttp } from '@ecosystem/ui-core'

// Вариант А: bearer-хост (SPA с OIDC-токенами)
configureEntityHttp({
  baseUrl: config.apiBaseUrl,          // '' = same-origin
  getAccessToken: () => auth.getAccessToken(),
  onUnauthorized: () => auth.signIn(location.pathname + location.search)
})

// Вариант Б: куки-хост (SPA IdentityServer)
configureEntityHttp({
  credentials: 'include',              // кука сессии
  onUnauthorized: () => router.push({ name: 'login' })
  // xhrHeader включён по умолчанию: X-Requested-With на мутациях — анти-CSRF куки-хостов
})

Маршруты-контракт

Компоненты навигируют по именам маршрутов listview и detailview — приложение обязано объявить их со следующими props:

import { ListViewPage, DetailViewPage } from '@ecosystem/ui-core'

const routes = [
  { path: '/listview/:entity', name: 'listview', component: ListViewPage, props: true },
  { path: '/detailview/:entity/:id', name: 'detailview', component: DetailViewPage, props: true }
]

Открытие строки (и «Создать») в ListViewPage показывает модальную карточку (DetailViewModal) поверх списка; кнопка «развернуть» в её шапке ведёт на отдельную страницу detailview — поэтому маршрут обязателен по-прежнему (плюс он же — deep-link на запись). Страница и модалка — хосты одной формы DetailViewForm (экспортируется отдельно: пропсы entity/id/variant, события config-loaded/created/saved/deleted) — при необходимости встраивайте её в свои сценарии.

Список доступных пользователю сущностей для навигации — entitiesApi.getEntities().

Список (ListViewPage)

Пропсы: entity (обязательный), actions?: ListAction[] (действия этого экземпляра; совпадение ключа замещает глобальные), hiddenActions?: string[] (скрыть действия по ключам).

Тулбар действий. Встроенные действия (ключ/порядок): create/10 (при permission.write), edit/20 (активно ровно при одной выбранной строке), delete/30 (активно при выборе ≥ 1; мягкое удаление по одной записи, ошибки — одним баннером), refresh/90. Колонка чекбоксов появляется автоматически, когда видимо хотя бы одно действие с requiresSelection; клик по строке по-прежнему открывает карточку, клик по чекбоксу — нет.

Колоночные фильтры — строка под шапкой, по одному условию на колонку, сервер объединяет по «И» (вместе с поиском). Операторы по типу компонента:

Компонент Операторы
String содержит (по умолчанию), начинается с, равно, не равно
String с params.kind='guid' равно, не равно (значение — валидный guid)
Int / Number / Time =, ≠, >, ≥, <, ≤
DateTime в этот день, после дня, начиная с дня, до дня, по день («равно» раскладывается в диапазон суток; «не равно» нет — нужен «ИЛИ»)
Date =, ≠, после, начиная с, до, по
Boolean / Enum один селект (Да/Нет либо значения enum)

«Пусто/не пусто» (eq/neq null) добавляются только для колонок с nullable: true — поле появилось в Core-Api 0.4.0; со старым бэкендом этих операторов просто нет, а guid-колонки ведут себя как строки (сервер ответит 400 на «содержит»). Регистронезависимые «содержит»/«начинается с»/поиск — тоже с Core-Api 0.4.0. Текст и числа применяются с дебаунсом 300 мс, остальное — сразу; «Сбросить фильтры» — в тулбаре справа.

Режимы загрузки. «Лента» (по умолчанию): порции догружаются при прокрутке (плюс кнопка «Показать ещё»); «Страницы» — классический пейджер. Размер порции/страницы — 25/50/100/200, по умолчанию 100. Режим и размер — глобальная настройка пользователя, localStorage-ключи ecosystem.listview.mode и ecosystem.listview.pageSize.

Оболочка (AppShell)

AppShell — каркас кабинета: тёмный сайдбар с меню (на мобильных — drawer с топбаром) и светлая область контента. Меню приложение собирает само (модель MenuItem; пункт с children — группа-секция), динамическую группу «по сущностям» даёт useEntityMenu() — пункты-ссылки на listview для всего из GET /api/entities (ответ кэшируется на уровне модуля: сайдбар и главная делят один запрос; reload() сбрасывает кэш).

<script setup lang="ts">
import { AppShell, useEntityMenu, IconHome, IconTable, type MenuItem } from '@ecosystem/ui-core'

const entities = useEntityMenu({ icon: IconTable })
const menu = computed<MenuItem[]>(() => [
  { label: 'Главная', to: { name: 'home' }, icon: IconHome, exact: true },
  ...(entities.items.value.length ? [{ label: 'Данные', children: entities.items.value }] : [])
])
</script>

<template>
  <AppShell :menu="menu">
    <template #logo><!-- логотип приложения --></template>
    <template #footer><!-- пользователь, кнопка выхода --></template>
    <RouterView />
  </AppShell>
</template>

Главная страница: по умолчанию монтируйте WelcomePage (простое приветствие, пропсы title/subtitle + слот) на свой home-роут; сервис со своей главной просто ставит на этот роут собственный компонент — ядро механики переопределения не требует. Иконки — встроенные inline-SVG компоненты (IconHome, IconUser, IconKey, IconMail, IconShield, IconTable, IconLogout, IconMenu, IconX, IconExpand, IconPlus, IconPencil, IconTrash, IconRefresh, IconFilter), размер — классом (class="size-4"); подойдёт и любой свой SVG-компонент.

Tailwind

Разметка компонентов — на Tailwind 4. В приложении-потребителе:

  1. Добавьте исходники пакета в сканирование: @source "../node_modules/@ecosystem/ui-core/src"; (или путь до packages/ui-core/src при file:-установке).
  2. Определите брендовые токены темы, которые использует разметка: --color-brand-500, --color-brand-600, --color-brand-700 (см. @theme Tailwind 4).

Расширение

Серверный ViewComponent.Name резолвится через реестр; свои компоненты добавляются точечно:

import { registerCell, registerEditor } from '@ecosystem/ui-core'

registerCell('Image', MyImageCell)
registerEditor('CodeEditor', MyCodeEditor)

Компонент не зарегистрирован — поле не рендерится (deny by default, как на сервере).

Свои действия списка

Реестр действий тулбара глобальный: registerListAction добавляет действие во все списки, повторная регистрация с тем же key замещает прежнее (в т.ч. встроенное), unregisterListAction(key) убирает. Область действия сужается предикатом criteria (сущность, права); порядок в тулбаре — числом order. Контекст ListActionContext даёт entity/config/rows/selectedIds/selectedRows, операции reload/openCreate/openDetail/clearSelection/setError и обёртку runBusy (блокирует тулбар на время работы).

import { registerListAction } from '@ecosystem/ui-core'

// Кнопка только для одной сущности: активна при выборе строк.
registerListAction({
  key: 'export-users',
  order: 50,
  label: 'Выгрузить',
  criteria: ctx => ctx.entity === 'user',
  requiresSelection: true,
  execute: ctx => ctx.runBusy(() => exportUsers(ctx.selectedIds))
})

Действию с собственным диалогом вместо execute задаётся component: при клике он монтируется с пропсом ctx и событием close и сам рисует свой диалог (Teleport в body — образец DetailViewModal). Скрыть встроенное действие на конкретной странице — проп hidden-actions='["delete"]'; заместить глобально — зарегистрировать свой вариант с тем же ключом (например, delete с criteria: ctx => ctx.entity !== 'role').

Dependencies

Development dependencies

ID Version
@tanstack/vue-table ^8.20.5
@vue/tsconfig ^0.7.0
typescript ~5.8.3
vue ^3.5.13
vue-router ^4.5.0
vue-tsc ^2.1.10

Peer dependencies

ID Version
@tanstack/vue-table ^8.20.5
vue ^3.5.13
vue-router ^4.5.0
Details
npm
2026-08-06 13:11:24 +03:00
8
31 KiB
Assets (1)
Versions (77) View all
0.60.0 2026-09-08
0.59.0 2026-09-06
0.58.1 2026-09-06
0.58.0 2026-09-06
0.57.1 2026-09-06