Backstage 软件目录(Software Catalog)定制指南:从分页、导出到自定义过滤器与完全自建 CatalogIndexPage
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
本文面向仍在使用**旧版前端系统(old frontend system)**的 Backstage 应用,系统讲解如何围绕默认的CatalogIndexPage组件进行深度定制:启用分页与目录导出、调整默认筛选器与初始 Kind、增删表格列、扩展行操作(actions),乃至基于EntityFilter接口与useEntityListHook 编写全新过滤器,最终完全自建一个属于你自己的目录首页。读完本文,你将掌握@backstage/plugin-catalog与@backstage/plugin-catalog-react中绝大多数目录页定制入口,并能直接套用到packages/app工程中。
注意:本文档面向仍在使用旧前端系统的 Backstage 应用。如果你的应用已经迁移到新前端系统,请阅读当前版本指南(Catalog Customization)。
定制前必读:默认 CatalogIndexPage 与 props 全景
Backstage 软件目录自带一个默认的CatalogIndexPage页面,用于筛选和查找目录实体(Entity),该页面由@backstage/create-app默认搭建完成。它提供了开箱即用的目录浏览体验,但如果你需要修改默认首页行为——例如设置初始选中的筛选器、调整表格列、增删行操作、或者为目录添加自定义过滤器——就需要了解这个组件暴露的全部定制入口。
从源码可以看到,DefaultCatalogPageProps完整定义了这些 props(见 DefaultCatalogPage.tsx):
| Prop | 类型 | 默认值 | 用途 |
|---|---|---|---|
initiallySelectedFilter | UserListFilterKind | 'owned' | 初始选中的用户列表筛选(owned / starred / all) |
initialKind | string | 'component' | 初始选中的实体 Kind |
columns | TableColumn[] \| CatalogTableColumnsFunc | 内置默认列 | 覆盖表格列 |
actions | TableProps['actions'] | view / edit / star | 覆盖表格行操作 |
tableOptions | TableProps['options'] | {} | 透传到底层表格的选项 |
emptyContent | ReactNode | — | 空列表占位内容 |
ownerPickerMode | EntityOwnerPickerProps['mode'] | 'owners-only' | 属主选择器模式 |
filters | ReactNode | DefaultFilters | 自定义筛选器集合 |
initiallySelectedNamespaces | string[] | — | 初始选中的命名空间 |
pagination | EntityListPagination | — | 分页配置(v1.21.0+) |
exportSettings | CatalogExportSettings | — | 目录导出配置 |
下文将围绕这些 props 逐一展开,并深入其对应的源码实现。
启用分页(Pagination)
目录首页的分页支持在 Backstagev1.21.0中加入,使用该特性前请确保你的版本不低于此。启用方式非常简单:给CatalogIndexPage传入paginationprop:
<Route path="/catalog" element={<CatalogIndexPage pagination />} />从源码实现看,pagination会被传递给EntityListProvider(见 DefaultCatalogPage.tsx),底层支持两种分页模式:offset(基于偏移量)与cursor(基于游标)。同时插件内部提供了OffsetPaginatedCatalogTable与CursorPaginatedCatalogTable两种表格实现(见 CatalogTable 目录)。pagination还可以传入对象形式,例如在自定义列一节中出现的pagination={{ mode: 'offset', limit: 20 }},用于指定分页模式与每页条数。
目录导出(Export)
目录导出功能允许用户将目录表格中的数据一键导出。启用方式是为CatalogIndexPage传入带enabled: true的exportSettingsprop:
<Route path="/catalog" element={<CatalogIndexPage exportSettings={{ enabled: true }} />} />启用后,页面头部会出现一个导出按钮。点击后会打开一个对话框,用户可以:
- 选择导出格式(默认提供CSV与JSON两种);
- 以复选框形式勾选/取消勾选要包含的列(默认全部预选)。
从源码看,导出功能由 CatalogExportButton.tsx 组件实现,它负责弹窗、格式下拉、列复选与导出触发;当exportSettings?.enabled为真时,该按钮会被渲染到页头(见 DefaultCatalogPage.tsx)。按钮默认展示在目录页右上角,带下载图标与文字标题。
导出配置接口:CatalogExportSettings
导出行为可以通过exportSettings配置CatalogExportSettings接口中的各种选项。该接口的源码定义位于 CatalogExportButton.tsx:
export interface CatalogExportSettings { enabled?: boolean; /** * Array of columns to include in the export. * * Each column requires an `entityFilterKey` (dot-separated path into the entity object that is returned by the catalog api) and an optional `title` for display. * When `title` is omitted, `entityFilterKey` is used as the display title. * * Default columns are: name, type, owner and description. **/ columns?: CatalogExportSettingsColumn[]; /** * Map of custom export format handlers. * * Each map entry provides an exporter function and an optional display label. * Custom formats appear in the export dialog alongside built-in CSV and JSON options. **/ exporters?: Record<string, CatalogExporterConfig>; /** Callback function invoked after successful export completion. Useful for displaying notifications or triggering post-export actions. */ onSuccess?: () => void; /** Callback function invoked if export fails. Receives an object containing the Error for error handling and user notification. */ onError?: (options: { error: Error }) => void; /** When true, hides the built-in CSV and JSON export options. Useful when only custom exporters should be available. */ disableBuiltinExporters?: boolean; }各字段说明:
enabled:是否显示导出按钮,默认false;columns:自定义导出列,不配置时使用默认列metadata.name(Name)、spec.type(Type)、spec.owner(Owner)、metadata.description(Description)。默认列定义在源码 CatalogExportButton.tsx 的DEFAULT_EXPORT_COLUMNS中;exporters:自定义导出格式处理器的映射表,键为格式名(如'xml'、'yaml'),值为{ exporter, label? }。label缺省时,格式下拉中会以键名大写显示;onSuccess/onError:导出成功/失败回调,可用于弹通知或执行后续动作。若未提供,组件内部会通过toastApi弹出默认的成功/失败提示(见 CatalogExportButton.tsx);disableBuiltinExporters:为true时隐藏内置的 CSV、JSON 选项,适合只希望暴露自定义导出格式的场景。
自定义导出列
默认导出包含 name、type、owner、description 四列。导出对话框打开时,所有已配置的列都会以复选框形式展示并处于预选状态,用户可以取消勾选不想导出的列再确认导出。
你可以自定义可用列:
import { CatalogIndexPage } from '@backstage/plugin-catalog'; const customColumns = [ { entityFilterKey: 'metadata.name', title: 'Name' }, { entityFilterKey: 'metadata.namespace', title: 'Namespace' }, { entityFilterKey: 'spec.owner', title: 'Owner' }, ]; <CatalogIndexPage exportSettings={{ enabled: true, columns: customColumns, }} />;列定义使用CatalogExportSettingsColumn结构:entityFilterKey是实体对象中的点分路径(dot-separated path),title是导出文件中的表头,缺省时直接用entityFilterKey作为表头。其源码定义见 serializeEntities.ts。实际取值时,代码通过getByPath按点分路径逐级解引用实体字段(见同文件 serializeEntities.ts);而在序列化 CSV 时,还会对以=、+、-、@开头的值加单引号前缀,防止 CSV/公式注入(见 serializeEntities.ts),这说明导出功能在实现层面就已考虑了安全细节。
自定义导出格式
除 CSV 和 JSON 外,你还可以通过提供自定义导出函数来增加新的导出格式。自定义导出器使用 async generator(异步生成器)实现流式下载:数据边生成边写入磁盘,在支持的浏览器中不会把整个导出内容缓存在内存里。
import { CatalogIndexPage, CatalogExporter, CatalogExporterConfig, } from '@backstage/plugin-catalog'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; // Custom exporter using async generator for streaming const xmlExporter: CatalogExporter = ({ apis, columns, streamRequest }) => { const catalogApi = apis.get(catalogApiRef); // Return an async generator that yields XML chunks async function* generateXml() { yield '<?xml version="1.0" encoding="UTF-8"?>\n<entities>\n'; for await (const page of catalogApi.streamEntities(streamRequest)) { for (const entity of page) { // Serialize each entity to XML and yield immediately yield serializeEntityToXml(entity, columns); } } yield '</entities>'; } return { generator: generateXml(), contentType: 'application/xml', }; }; const yamlExporter: CatalogExporter = ({ apis, columns, streamRequest }) => { const catalogApi = apis.get(catalogApiRef); async function* generateYaml() { for await (const page of catalogApi.streamEntities(streamRequest)) { for (const entity of page) { yield serializeEntityToYaml(entity, columns); yield '---\n'; // YAML document separator } } } return { generator: generateYaml(), contentType: 'application/x-yaml', }; }; const exporters: Record<string, CatalogExporterConfig> = { xml: { exporter: xmlExporter, label: 'XML' }, yaml: { exporter: yamlExporter, label: 'YAML' }, }; <CatalogIndexPage exportSettings={{ enabled: true, exporters, }} />;提供自定义格式后,它们会与内置的 CSV、JSON 选项一起出现在导出对话框中(若同时设置了disableBuiltinExporters: true,则只会显示自定义格式)。
从源码层面理解这一机制:CatalogExporter类型要求导出函数返回一个{ generator: AsyncGenerator<string, void, unknown>; contentType: string }结构(见 useStreamingExport.ts)。执行导出时,useStreamingExportHook 会把生成器通过createStreamFromAsyncGenerator包装成流并调用streamDownload触发浏览器下载(见 useStreamingExport.ts)。内置的 CSV/JSON 导出同样是基于 async generator 实现的——streamEntitiesCsvGenerator逐页调用catalogApi.streamEntities并立即yield序列化结果,JSON 导出器还会先yield '['再在结尾yield '\n]'以保持合法 JSON 结构(见 useStreamingExport.ts)。
值得一提的细节是:如果调用方没有显式提供streamRequest,导出会通过toStreamRequest(filters)从当前EntityList的筛选状态推导请求参数(见 useStreamingExport.ts),这意味着导出的数据与用户当前在目录页上的筛选视图保持一致,而不是导出全量数据。
成功/失败回调
你还可以提供回调来处理导出成功或失败的情况:
<CatalogIndexPage exportSettings={{ enabled: true, onSuccess: () => { // Handle successful export notificationApi.success({ message: 'Export completed!' }); }, onError: ({ error }) => { // Handle export error notificationApi.error({ message: `Export failed: ${error.message}`, }); }, }} />onSuccess在导出流程成功结束后被调用;onError接收{ error: Error },便于你自定义错误提示或上报。从源码看,导出完成/失败后组件会依次触发这两个回调,若未提供则回退到内置 toast 提示(见 CatalogExportButton.tsx)。
组合示例
以下示例组合了全部自定义选项:
<CatalogIndexPage exportSettings={{ enabled: true, columns: [ { entityFilterKey: 'metadata.name', title: 'Name' }, { entityFilterKey: 'spec.type', title: 'Type' }, { entityFilterKey: 'spec.owner', title: 'Owner' }, { entityFilterKey: 'metadata.namespace', title: 'Namespace' }, ], exporters: { xml: { exporter: xmlExporter, label: 'XML' }, yaml: { exporter: yamlExporter, label: 'YAML' }, }, onSuccess: () => { notificationApi.success({ message: 'Export completed!' }); }, onError: ({ error }) => { notificationApi.error({ message: `Export failed: ${error.message}`, }); }, }} />设置初始选中的筛选器(Initially Selected Filter)
默认情况下,目录页初始选中的筛选器是Owned。如果你的目录还在建设初期、实体不多,这可能导致首页一开始显示空列表。如果你希望默认显示All,可以这样修改:
<Route path="/catalog" element={<CatalogIndexPage initiallySelectedFilter="all" />} />可选值为:owned、starred、all。从源码看,该 prop 的默认值为'owned',并会被透传给DefaultFilters中的UserListPicker(见 DefaultCatalogPage.tsx 与 DefaultFilters.tsx)。
设置初始选中的 Kind(Initially Selected Kind)
默认情况下,进入目录页时初始选中的 Kind 是Component,但你的组织可能有不同的需求——例如希望始终默认选中Domain,可以这样配置:
<Route path="/catalog" element={<CatalogIndexPage initialKind="domain" />} />可选值包括系统模型中的所有默认 Kind,以及你自定义添加的任何 Kind。源码中该 prop 默认值为'component'(见 DefaultCatalogPage.tsx),并最终传递到EntityKindPicker的initialFilter(见 DefaultFilters.tsx)。
属主选择器模式(Owner Picker Mode)
Owner(属主)筛选器默认只包含实际拥有目录中实体的用户和/或用户组。如果你需要显示全部用户/组,可以这样配置:
<Route path="/catalog" element={<CatalogIndexPage ownerPickerMode="all" />} />可选值为:owners-only(默认)或all。该值会作为EntityOwnerPicker的modeprop 传入(见 DefaultFilters.tsx)。
表格选项(Table Options)
Backstage 中的表格基于@material-table/core构建,CatalogIndexPage提供了tableOptionsprop 让你在一定程度上定制底层表格,但部分 Backstage 硬编码的设置无法修改。下面示例展示了如何用该 prop 禁用表格表头的搜索框:
<Route path="/catalog" element={<CatalogIndexPage tableOptions={{ search: false }} />} />tableOptions可设置大量选项,其完整列表对应@material-table/core的Options接口(Backstage 当前使用的版本为v3.1.0)。实际使用时建议以该接口的类型定义为准,CatalogIndexPage会把它原样透传给CatalogTable(见 DefaultCatalogPage.tsx)。
自定义表格列(Customize Columns)
CatalogIndexPage中看到的列是面向大多数场景精选的起点,但你完全可能希望为已有或自定义 Kind 增删列。
为已有 Kind 添加列
假设我们想为UserKind 添加一列 "User Email"。做法是覆盖传入CatalogIndexPage的columns。首先,匹配要覆盖的实体 Kind,并定义要展示的列:
const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => { if (entityListContext.filters.kind?.value === 'user') { return [ // Render existing columns ...CatalogTable.defaultColumnsFunc(entityListContext), // Add new columns here ]; } return CatalogTable.defaultColumnsFunc(entityListContext); };然后实现createUserEmailColumn函数并把它加入列列表。field用于从实体中取数据,render则允许你自定义数据的展示方式:
const createUserEmailColumn = (): TableColumn<CatalogTableRow> => ({ title: 'User Email', field: 'entity.spec.profile.email', render: ({ entity }) => ( <OverflowTooltip text={entity.spec?.profile?.['email'] || 'N/A'} placement="bottom-start" /> ), }); const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => { if (entityListContext.filters.kind?.value === 'user') { return [ // Render existing columns ...CatalogTable.defaultColumnsFunc(entityListContext), // Add new columns here createUserEmailColumn(), ]; } return CatalogTable.defaultColumnsFunc(entityListContext); };最后,把myColumnsFunc传给CatalogIndexPage:
const routes = ( <FlatRoutes> <Route path="/catalog" element={ <CatalogIndexPage pagination={{ mode: 'offset', limit: 20 }} columns={myColumnsFunc} /> } /> {/* Other routes */} </FlatRoutes> )这里涉及两个关键类型(见 CatalogTable/types.ts):
CatalogTableRow:表格行的数据结构,包含entity(完整实体)与resolved(预解析的 name、entityRef、所属关系等信息,见 types.ts);CatalogTableColumnsFunc:接收entityListContext(含当前筛选状态filters)并返回列数组的函数类型(见 types.ts)。
为自定义或特定 Kind 添加列
另一个典型场景是为自定义Kind添加列,该能力在 Backstagev1.23.0及以上可用。例如:
import { CatalogEntityPage, CatalogIndexPage, catalogPlugin, CatalogTable, CatalogTableColumnsFunc, } from '@backstage/plugin-catalog'; const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => { if (entityListContext.filters.kind?.value === 'MyKind') { return [ CatalogTable.columns.createNameColumn(), CatalogTable.columns.createOwnerColumn(), ]; } return CatalogTable.defaultColumnsFunc(entityListContext); }; <Route path="/catalog" element={<CatalogIndexPage columns={myColumnsFunc} />} />CatalogTable.columns提供了如createNameColumn、createOwnerColumn等可复用的内置列工厂,方便你为特定 Kind 快速组装列集合。
:::note 以上示例中的文件内容为便于说明均做了精简。 :::
自定义行操作(Customize Actions)
CatalogIndexPage默认带三个行操作:view(查看)、edit(编辑)和star(收藏)。你可能会想添加更多。
首先,需要把@mui/utils添加到packages/app/package.json:
yarn --cwd packages/app add @mui/utils然后进行如下修改:
import { AlertDisplay, OAuthRequestDialog, SignInPage, TableProps, } from '@backstage/core-components'; import { CatalogEntityPage, CatalogIndexPage, CatalogTableRow, catalogPlugin, } from '@backstage/plugin-catalog'; import { Typography } from '@material-ui/core'; import OpenInNew from '@material-ui/icons/OpenInNew'; import { visuallyHidden } from '@mui/utils'; const customActions: TableProps<CatalogTableRow>['actions'] = [ ({ entity }) => { const url = 'https://backstage.io/'; const title = `View - ${entity.metadata.name}`; return { icon: () => ( <> <Typography style={visuallyHidden}>{title}</Typography> <OpenInNew fontSize="small" /> </> ), tooltip: title, disabled: !url, onClick: () => { if (!url) return; window.open(url, '_blank'); }, }; }, ]; <Route path="/catalog" element={<CatalogIndexPage actions={customActions} />} />:::note 以上App.tsx示例内容为便于说明做了精简。 :::
需要特别说明:上述自定义会覆盖现有操作。目前,如果想保留默认操作并添加自己的操作,唯一的办法是把默认操作(defaultActions,即 view / edit / star 的实现)也复制到你的操作数组中。默认操作的源码位于 CatalogTable.tsx 的defaultActions定义处,可直接参考其实现来保留原有行为。
自定义筛选器(Customize Filters)
自定义筛选器有多种方式:通过 props 调整现有筛选器、增删默认筛选器、创建全新的自定义筛选器。下面分情况说明。
默认筛选器 Props(Default Filters)
@backstage/plugin-catalog-react提供了一组默认筛选器DefaultFilters,它聚合了前文提到的各种 props。用法如下:
import { DefaultFilters } from '@backstage/plugin-catalog-react'; <Route path="/catalog" element={ <CatalogIndexPage filters={ <> <DefaultFilters initialKind="Domain" initiallySelectedFilter="all" ownerPickerMode="all" /> </> } /> } />;从源码看,DefaultFilters实际渲染了 8 个筛选器组件:EntityKindPicker、EntityTypePicker、UserListPicker、EntityOwnerPicker、EntityLifecyclePicker、EntityTagPicker、EntityProcessingStatusPicker和EntityNamespacePicker(见 DefaultFilters.tsx)。
移除默认筛选器
如果你不想使用 Lifecycle(生命周期)、Tag(标签)和 Processing Status(处理状态)筛选器,可以这样移除:
import { EntityKindPicker, EntityTypePicker, UserListPicker, EntityOwnerPicker, EntityNamespacePicker, } from '@backstage/plugin-catalog-react'; <Route path="/catalog" element={ <CatalogIndexPage filters={ <> <EntityKindPicker /> <EntityTypePicker /> <UserListPicker /> <EntityOwnerPicker /> <EntityNamespacePicker /> </> } /> } />;当你显式传入filters时,DefaultCatalogPage会使用你提供的筛选器集合替换默认的DefaultFilters(见 DefaultCatalogPage.tsx),因此只保留你列出的筛选器即可。
自定义筛选器
你可以添加自定义筛选器。例如,假设我们想按实体上的自定义注解company.com/security-tier进行筛选,可以按以下步骤构建筛选器。
首先,创建一个实现EntityFilter接口的新筛选器:
import { EntityFilter } from '@backstage/plugin-catalog-react'; import { Entity } from '@backstage/catalog-model'; class EntitySecurityTierFilter implements EntityFilter { constructor(readonly values: string[]) {} filterEntity(entity: Entity): boolean { const tier = entity.metadata.annotations?.['company.com/security-tier']; return tier !== undefined && this.values.includes(tier); } }EntityFilter接口支持两类实现方式(见 catalog-react 的 types.ts):
- 后端筛选(backend filter):通过实现
getCatalogFilters()返回查询参数,筛选条件会被下推到catalog-backend,在查询阶段完成过滤; - 前端筛选(frontend filter):通过实现
filterEntity(entity)在实体从后端加载后于前端过滤。
上面的EntitySecurityTierFilter使用了前端过滤方式(只实现filterEntity)。仓库中内置筛选器则提供了两种方式的参考:例如EntityTagFilter同时实现了filterEntity与getCatalogFilters(见 catalog-react 的 filters.ts),既可在后端过滤也可在前端兜底;而EntityKindFilter、EntityTypeFilter只实现getCatalogFilters,属于纯后端筛选(见 filters.ts)。
接下来,以类型安全的方式用这个筛选器扩展默认筛选器集合。在筛选器旁边创建扩展默认结构的自定义筛选器类型:
export type CustomFilters = DefaultEntityFilters & { securityTiers?: EntitySecurityTierFilter; };为了控制这个筛选器,可以创建一个显示安全等级复选框的 React 组件。该组件会用到useEntityListHook,并把扩展后的筛选器类型作为泛型参数传入:
export const EntitySecurityTierPicker = () => { // The securityTiers key is recognized due to the CustomFilter generic const { filters: { securityTiers }, updateFilters, } = useEntityList<CustomFilters>(); // Toggles the value, depending on whether it's already selected function onChange(value: string) { const newTiers = securityTiers?.values.includes(value) ? securityTiers.values.filter(tier => tier !== value) : [...(securityTiers?.values ?? []), value]; updateFilters({ securityTiers: newTiers.length ? new EntitySecurityTierFilter(newTiers) : undefined, }); } const tierOptions = ['1', '2', '3']; return ( <FormControl component="fieldset"> <Typography variant="button">Security Tier</Typography> <FormGroup> {tierOptions.map(tier => ( <FormControlLabel key={tier} control={ <Checkbox checked={securityTiers?.values.includes(tier)} onChange={() => onChange(tier)} /> } label={`Tier ${tier}`} /> ))} </FormGroup> </FormControl> ); };现在把该组件加入CatalogIndexPage:
import { DefaultFilters } from '@backstage/plugin-catalog-react'; const routes = ( <FlatRoutes> <Navigate key="/" to="catalog" /> <Route path="/catalog" element={ <CatalogIndexPage filters={ <> <DefaultFilters /> <EntitySecurityTierPicker /> </> } /> } /> {/* ... */} </FlatRoutes> );同样的方法也可以用来以不同接口定制_默认_筛选器——此时无需泛型参数,因为筛选器结构与默认结构保持一致(例如直接使用useEntityList<DefaultEntityFilters>()并覆盖默认筛选键即可)。
高级定制:完全自建 CatalogIndexPage
如果以上方案都无法满足你的需求,你还可以选择创建完全自定义的CatalogIndexPage。
import { PageWithHeader, Content, ContentHeader, SupportButton, } from '@backstage/core-components'; import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { CatalogTable } from '@backstage/plugin-catalog'; import { EntityListProvider, CatalogFilterLayout, EntityKindPicker, EntityLifecyclePicker, EntityNamespacePicker, EntityOwnerPicker, EntityProcessingStatusPicker, EntityTagPicker, EntityTypePicker, UserListPicker, } from '@backstage/plugin-catalog-react'; export const CustomCatalogPage = () => { const orgName = useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage'; return ( <PageWithHeader title={orgName} themeId="home"> <Content> <ContentHeader title=""> <SupportButton>All your software catalog entities</SupportButton> </ContentHeader> <EntityListProvider pagination> <CatalogFilterLayout> <CatalogFilterLayout.Filters> <EntityKindPicker /> <EntityTypePicker /> <UserListPicker /> <EntityOwnerPicker /> <EntityLifecyclePicker /> <EntityTagPicker /> <EntityProcessingStatusPicker /> <EntityNamespacePicker /> </CatalogFilterLayout.Filters> <CatalogFilterLayout.Content> <CatalogTable /> </CatalogFilterLayout.Content> </CatalogFilterLayout> </EntityListProvider> </Content> </PageWithHeader> ); };上述是一个非常基础的全自定义CatalogIndexPage版本。这个示例建立在默认页面所使用的构建模块之上——即 DefaultCatalogPage.tsx 中的BaseCatalogPage实现。你可以深入探索各个组件的 props,看看还能做哪些事情(例如自定义EntityListProvider的pagination、自定义CatalogTable的columns/actions、在ContentHeader中追加导出按钮等)。
:::note 目录首页被设计为具有极小的代码足迹以便于定制,但复制一份页面也意味着存在随版本演进而逐渐过时的风险。建议定期查看 catalog 插件的 CHANGELOG,关注CatalogIndexPage相关 API 的变更。 :::
要使用这个名为CustomCatalogPage的自定义页面,需要修改路由:
const routes = ( <FlatRoutes> <Navigate key="/" to="catalog" /> <Route path="/catalog" element={<CatalogIndexPage />}> <CustomCatalogPage /> </Route> {/* ... */} </FlatRoutes> );总结
围绕CatalogIndexPage,Backstage 提供了一条从「零配置开箱即用」到「完全自建页面」的平滑定制路径:
- 轻量调整:通过
pagination、initiallySelectedFilter、initialKind、ownerPickerMode、tableOptions等 props 快速改变默认行为; - 数据导出:通过
exportSettings启用导出,并可自定义导出列、基于 async generator 的自定义格式(如 XML、YAML)、成功/失败回调,甚至禁用内置格式; - 展示层定制:通过
columns函数为已有或自定义 Kind 增删列,通过actions覆盖行操作; - 筛选逻辑定制:使用
DefaultFilters调整默认筛选器、移除不需要的筛选器,或基于EntityFilter接口 +useEntityListHook 编写完全自定义的筛选组件; - 终极方案:以
EntityListProvider、CatalogFilterLayout、CatalogTable等构建模块为积木,自建一个完全属于你的目录首页。
所有 props 与类型的最终定义都可以在 DefaultCatalogPage.tsx、CatalogExportButton.tsx 与 CatalogTable/types.ts 中找到,配合这些源码可以更准确地理解每个配置项的实际影响。如果你的应用已迁移到新前端系统,请转而阅读新版指南以获取对应实现方式。
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考