Headlamp 插件开发常见模式(Common Plugin Patterns)实战指南:基于官方示例的 UI 扩展、主题定制与高级集成

Headlamp 插件开发常见模式(Common Plugin Patterns)实战指南:基于官方示例的 UI 扩展、主题定制与高级集成

【免费下载链接】headlamp A Kubernetes web UI that is fully-featured, user-friendly and extensible 【免费下载链接】headlamp 项目地址: https://gitcode.com/GitHub_Trending/he/headlamp

Headlamp 是一个功能完整、用户友好且可扩展的 Kubernetes Web UI,其插件机制允许开发者以 JavaScript/TypeScript 模块的形式深度定制界面。本文以官方文档《Common Plugin Patterns》为核心骨架,结合仓库内 plugins/examples 目录下的 12 个可直接运行的示例插件,系统讲解顶部导航栏、侧边栏、详情页、表格、应用菜单、动态集群、插件设置、主题、Logo、图表、UI 面板与集群选择器等常见开发模式。读完本文,你将掌握 Headlamp 插件 API(registerAppBarActionregisterSidebarEntryregisterDetailsViewSectionregisterAppTheme 等)的使用方法,并能够照抄示例快速落地自己的插件功能。

快速上手:如何运行官方示例

所有模式都在仓库的 plugins/examples/ 目录下配有可运行的完整示例。文档给出的标准运行流程如下:

# 1. 进入某个示例插件目录
cd plugins/examples/[example-name]

# 2. 安装依赖
npm install

# 3. 启动插件开发服务器
npm start

npm startheadlamp-plugin 工具提供。以 plugins/examples/pod-counter/package.json 为例,其 scripts 段暴露了一整套开发命令,而不仅是 start

"scripts": {
  "start": "headlamp-plugin start",
  "build": "headlamp-plugin build",
  "format": "headlamp-plugin format",
  "lint": "headlamp-plugin lint",
  "tsc": "headlamp-plugin tsc",
  "storybook": "headlamp-plugin storybook",
  "storybook-build": "headlamp-plugin storybook-build",
  "test": "headlamp-plugin test",
  "i18n": "headlamp-plugin i18n"
}

其中 start 会在开发模式下监听文件变化、自动重新构建插件;build 产出可直接分发的构建产物。运行后打开 Headlamp(桌面端会自动发现处于开发模式的插件,或按 development 指南 启动开发服务器),即可在界面中看到插件效果。若想从零创建一个新插件,可参考 Getting Started 中的脚手架命令:

npx --yes @kinvolk/headlamp-plugin create my-first-plugin

下文所有 API 均可在 @kinvolk/headlamp-plugin/lib 中导入(部分公共组件从 @kinvolk/headlamp-plugin/lib/CommonComponents 导入,例如 SectionBoxTableNameValueTableActionButtonTileChart)。

UI 扩展模式

UI 扩展是插件最常用的场景,覆盖顶部导航栏、侧边栏、资源详情页、资源表格与应用菜单五大区域。

1. 向顶部导航栏(App Bar)添加内容

典型用途:集群统计信息、系统健康状态、快捷操作、全局信息。

示例插件 plugins/examples/pod-counter 通过 registerAppBarAction 在顶部导航栏展示集群中的 Pod 总数,并使用 K8s.ResourceClasses.Pod.useList() 获取实时数据(见 src/index.tsx):

import {
  DefaultAppBarAction,
  K8s,
  registerAppBarAction,
  registerPluginSettings,
} from '@kinvolk/headlamp-plugin/lib';
import Message from './Message';

function PodCounter() {
  const [pods, error] = K8s.ResourceClasses.Pod.useList();
  const msg = pods === null ? 'Loading…' : pods.length.toString();
  return <Message msg={msg} error={error !== null} />;
}

registerAppBarAction(PodCounter);

代码要点:

  • useList() 返回 [数据, 错误] 二元组;pods === null 表示仍在加载,从而自然地处理加载中状态;
  • 传入的组件收到错误后由 Message 组件渲染错误样式,即文档所述的“处理加载与错误”;
  • 组件直接复用 Headlamp 的 K8s 数据层,无需自行封装请求。

registerAppBarAction 还可以接收一个“重排函数”,对导航栏中的既有操作按钮重新排序或移除。示例演示了如何将“通知”按钮移到末尾:

registerAppBarAction(function reorderNotifications({ actions }) {
  if (!actions) {
    return actions;
  }
  // 移除默认的通知按钮
  const newActions = actions.filter(action => action.id !== DefaultAppBarAction.NOTIFICATION);
  // 也可以 push 一个自定义按钮:newActions.push({action: <PodCounter />, id: 'pod-counter'});
  const notificationAction = actions.find(action => action.id === DefaultAppBarAction.NOTIFICATION);
  if (notificationAction) {
    newActions.push(notificationAction);
  }
  return newActions;
});

运行该示例后,顶部栏会显示类似“# Pods: 9”的统计信息,效果见下图(截图位于 docs/development/plugins/images/podcounter_screenshot.png):

Pod Counter 示例插件在顶部导航栏显示 Pod 数量统计

2. 自定义侧边栏导航

典型用途:自定义仪表盘、外部工具链接、特殊资源视图、管理工具、甚至全新的侧边栏。

示例插件 plugins/examples/sidebar 是侧边栏能力的“百科全书”,其入口文件 src/index.tsx 展示了 registerSidebarEntryregisterSidebarEntryFilterregisterRouteregisterRouteFilterregisterHomeSidebarEntryFilter 的完整用法。下图是该示例的运行效果(截图位于 docs/development/plugins/images/sidebar.png):

Sidebar 示例插件为侧边栏新增 Feedback 等自定义导航项

添加顶级侧边栏项:通过 registerSidebarEntry 传入 parent: null 的条目即成为顶级项;urlicon 分别控制跳转地址与图标(图标使用 MDI 图标集,如 mdi:comment-quote):

registerSidebarEntry({
  parent: null,
  name: 'feedback',
  label: 'Feedback',
  url: '/feedback',
  icon: 'mdi:comment-quote',
});

添加子级(嵌套)项:用 parent 指向父条目名称即可;若父条目尚未创建,parent 会先创建它:

registerSidebarEntry({
  parent: 'feedback2',   // 挂到 feedback2 下
  name: 'feedback3',
  label: 'More Feedback',
  url: '/feedback3',
});

添加不可点击的分组标题:设置 entryType: 'subheader',并可用 sx 调整字号与大小写:

registerSidebarEntry({
  parent: null,
  name: 'feedback-section',
  label: 'Feedback Tools',
  entryType: 'subheader',
  sx: { fontSize: '0.8rem', textTransform: 'none' },
});

创建全新的侧边栏:只需在条目中给出一个尚不存在的 sidebar 名称,Headlamp 会为该名称创建全新侧边栏并放入条目。示例创建了名为 myplugin 的独立侧边栏,并为它注册了“Back to Clusters”和“Special Area”两个入口:

registerSidebarEntry({
  name: 'backtoclusters',
  label: 'Back to Clusters',
  url: '/',
  icon: 'mdi:hexagon',
  sidebar: 'myplugin',   // 全新侧边栏
});
registerSidebarEntry({
  name: 'mypluginarea',
  label: 'Special Area',
  url: '/mypluginarea',
  icon: 'mdi:comment-quote',
  sidebar: 'myplugin',
});

同时用 registerRoute 为每个 URL 注册对应的页面组件。路由支持几个关键选项:

  • sidebar:指定该路由对应的侧边栏条目(也可传 {item, sidebar} 对象);
  • useClusterURL: false:URL 中不包含 /c/<cluster>/ 前缀;
  • noAuthRequired: true:该视图无需认证即可访问;
  • hideAppBar: true:隐藏顶部 AppBar;
  • exact: true:精确匹配路径。

例如“我的插件区”路由同时启用了前三项:

registerRoute({
  path: '/mypluginarea',
  sidebar: { item: 'mypluginarea', sidebar: 'myplugin' },
  useClusterURL: false,
  noAuthRequired: true,
  name: 'mypluginarea',
  exact: true,
  component: () => (
    <SectionBox title="Special Plugin Area" textAlign="center" paddingTop={2}>
      <Typography>See how the home sidebar is completely new?</Typography>
    </SectionBox>
  ),
});

移除侧边栏项与路由registerSidebarEntryFilterregisterRouteFilter 接收“过滤函数”,返回 null 即删除该项。示例把内置的 Workloads、Namespaces 入口及其路由一并移除,并在 Home 侧边栏(HOME)中移除“settings”:

registerSidebarEntryFilter(entry => (entry.name === 'workloads' ? null : entry));
registerRouteFilter(route => (route.path === '/workloads' ? null : route));

registerSidebarEntryFilter(entry => (entry.name === 'namespaces' ? null : entry));
registerRouteFilter(route => (route.path === '/namespaces' ? null : route));

registerHomeSidebarEntryFilter(entry => (entry.name === 'settings' ? null : entry));

注意:registerSidebarEntryFilter 也可在组件内部动态调用(如在 useEffect 中移除“Click me and I will disappear”条目),从而实现运行期动态增删。

3. 增强资源详情页

典型用途:附加资源元数据、外部系统链接、自定义操作按钮、关联资源信息。

示例插件 plugins/examples/details-viewsrc/index.tsx 演示了 registerDetailsViewSectionregisterDetailsViewHeaderAction(以及更进阶的 registerDetailsViewSectionsProcessorregisterDetailsViewHeaderActionsProcessor)。下图展示了详情页中新增自定义子标题的运行效果(截图位于 docs/development/plugins/images/details-view.jpeg):

Details View 示例插件在 Pod 详情页添加自定义章节

向详情页添加自定义章节registerDetailsViewSection 接收一个回调,回调返回 React 元素则渲染,返回 null 则跳过。示例只针对 ConfigMap 资源渲染自定义区块:

registerDetailsViewSection(({ resource }: DetailsViewSectionProps) => {
  if (resource && resource.kind === 'ConfigMap') {
    return (
      <SectionBox title="A custom very fine section title">
        The body of our custom Section for {resource.kind}
      </SectionBox>
    );
  }
  return null;
});

向头部添加操作按钮registerDetailsViewHeaderAction 注册的组件会出现在详情页头部操作区:

function IconAction() {
  return (
    <ActionButton
      description="Our action button"
      icon="mdi:comment-quote"
      onClick={() => console.log('Hello from IconAction!')}
    />
  );
}
registerDetailsViewHeaderAction(IconAction);

调整章节顺序与替换默认按钮(进阶):

  • registerDetailsViewSectionsProcessor(resource, sections):拿到当前章节数组,可插入、删除或重排章节。示例借助 DefaultDetailsViewSection.MAIN_HEADER 定位主头部,并在其后插入自定义章节;
  • registerDetailsViewHeaderActionsProcessor(resource, actions):拿到当前头部操作按钮数组,可替换默认按钮。示例使用 DetailsViewDefaultHeaderActions.DELETE 定位删除按钮,移除它并替换为自定义按钮(默认按钮还包括 EDIT、RESTART、SCALE 等)。
registerDetailsViewHeaderActionsProcessor(function replaceDeleteAction(resource, actions) {
  if (!resource || !actions.find(action => action.id === DetailsViewDefaultHeaderActions.DELETE)) {
    return actions;   // 没有删除按钮就不做任何事
  }
  const actionsDeleteRemoved = actions.filter(action => action.id !== DetailsViewDefaultHeaderActions.DELETE);
  return [
    ...actionsDeleteRemoved,
    {
      id: 'my-custom-delete-action',
      action: (
        <ActionButton
          description="Useless button from a plugin example from details-view example plugin"
          icon="mdi:delete"
          onClick={() => alert(`One cannot simply delete a ${resource.kind}!`)}
        />
      ),
    },
  ];
});

4. 定制资源表格

典型用途:表格行的上下文菜单、自定义表格列、行级操作、更好的数据展示。

示例插件 plugins/examples/tables 通过 registerResourceTableColumnsProcessor 为 Pods 列表的每一行追加一个三点“上下文菜单”(Details / Delete),代码见 src/index.tsx

registerResourceTableColumnsProcessor(function setupContextMenuForPodsList({ id, columns }) {
  if (id === 'headlamp-pods') {
    const podColumns = columns as ResourceTableColumn<Pod>[];
    podColumns.push({
      label: '',
      getValue: (pod: Pod) => pod.getDetailsLink(),
      render: (pod: Pod) => <ContextMenu detailsLink={pod.getDetailsLink()} />,
    });
  }
  return columns;
});

使用要点:

  • 表格 ID 约定:资源列表表格的 ID 遵循 headlamp-${资源复数名} 的约定,例如 Pods 列表是 headlamp-pods、Namespaces 列表是 headlamp-namespaces;而集群总览中的事件表是 headlamp-cluster.overview.events。开发期可以在 processor 里打印 id 以确认目标表格;
  • 若不判断 id,则所有资源表格都会被修改;插件自己创建的表格也可以使用带自身前缀的 ID;
  • 新增列对象包含 labelgetValuerender 三个字段,render 负责渲染自定义组件(此处为 ContextMenu,一个使用 MUI Menu/MenuItem 实现的下拉菜单)。

运行效果见下表截图(位于 docs/development/plugins/images/table-context-menu.png):

Tables 示例插件为 Pods 表格每一行添加上下文菜单

5. 应用集成(桌面端菜单)

典型用途:运行本地命令、桌面端菜单、外部工具快捷键、系统集成。

示例插件 plugins/examples/app-menus 通过 Headlamp.setAppMenu 在桌面端顶部菜单栏新增“Chat with us”菜单,代码见 src/index.tsx。它演示了基于 Plugin 基类的插件写法:

import { Headlamp, Plugin } from '@kinvolk/headlamp-plugin/lib';

class AppMenuDemo extends Plugin {
  initialize(): boolean {
    console.log('app-menus plugin initialized');

    if (!Headlamp.isRunningAsApp()) {
      window.alert('app-menus plugin: Headlamp is running as an app. This plugin will not do anything!');
      return;
    }

    Headlamp.setAppMenu(menus => {
      let chatMenu = menus?.find(menu => menu.id === 'custom-menu-item') || null;
      if (!chatMenu) {
        chatMenu = {
          label: 'Chat with us',
          id: 'custom-menu-item',
          submenu: [
            { label: 'This menu is an example from the app-menus plugin', enabled: false },
            { label: 'Open Headlamp Slack', url: 'https://kubernetes.slack.com/messages/headlamp' },
          ],
        };
        menus.push(chatMenu);
      }
      return menus;
    });
  }
}

Headlamp.registerPlugin('app-menus', new AppMenuDemo());

要点:

  • Headlamp.isRunningAsApp() 用于判断是否运行在桌面应用中(应用菜单只在桌面端生效);
  • Headlamp.setAppMenu 接收一个“变换函数”:入参是当前菜单数组,返回修改后的数组;菜单项结构为 {label, id, submenu},子菜单项可设置 enabled: false(置灰)或 url(点击后打开外部链接);
  • 使用 Plugin 子类时,在 initialize() 中完成注册,并通过 Headlamp.registerPlugin(name, instance) 挂载。运行效果见 docs/development/plugins/images/app-menus.png

6. 动态集群管理

典型用途:自定义集群发现、动态集群注册、集群管理 UI、多集群部署。

示例插件 plugins/examples/dynamic-clusters 在顶部栏注册一个“New cluster”按钮,弹窗中根据后端是否开启动态集群(stateless)能力,提供两种接入方式,代码见 src/index.tsx

// 挂载时向后端询问动态集群是否开启
useEffect(() => {
  request('/config', {}, false, false).then((response: any) => {
    setIsDynamicClusterEnabled(response.isDynamicClusterEnabled);
  });
}, []);
  • 动态集群开启时:要求用户粘贴 base64 编码的 kubeconfig,插件先用 atob 解码并用 js-yaml 校验(apiVersion: v1kind: Config),随后调用 Headlamp.setCluster({ kubeconfig }) 注册集群;
  • 未开启时:退化为 Headlamp.setCluster({ name, server }) 的经典方式。

两种方式成功后都会 window.location.reload() 刷新界面;失败则通过 catch 捕获并回显错误信息。这展示了插件如何与后端 /config 接口联动,并结合 ApiProxy.requestHeadlamp.setCluster 完成动态集群生命周期管理。

7. 插件设置(Plugin Settings)

典型用途:用户偏好、功能开关、配置表单、持久化设置。

示例插件 plugins/examples/pod-counterplugins/examples/change-logo 都演示了 registerPluginSettings

以 pod-counter 为例,registerPluginSettings('@kinvolk/headlamp-pod-counter', Settings, true) 将 Settings 组件注册到“插件设置”面板。Settings 组件接收 {data, onDataChange} 两个 props:data 是当前配置,onDataChange 用于提交新配置:

function Settings(props) {
  const { data, onDataChange } = props;

  const handleChange = event => {
    onDataChange?.({ ...data, errorMessage: event.target.value });
  };

  const settingsRows = [
    {
      name: 'Custom Error Message',
      value: (
        <TextField
          fullWidth
          helperText="Enter the custom error message to display when the pod count cannot be retrieved."
          value={data?.errorMessage ?? 'Uh... pods!?'}
          onChange={handleChange}
          variant="standard"
        />
      ),
    },
  ];

  return (
    <Box width={'80%'}>
      <NameValueTable rows={settingsRows} />
    </Box>
  );
}

change-logo 示例则展示了基于 ConfigStore 的持久化方案(见 settings.tsx):new ConfigStore<pluginConfig>('change-logo') 创建配置存储,store.get() 读取、store.set() 写入;设置表单里的 AutoSaveInput 组件对输入做 1000ms 防抖后自动保存,实现“边输入边持久化”的体验。

样式与主题模式

1. 自定义主题

典型用途:企业品牌定制、自定义配色、无障碍(可访问性)、深色/浅色主题。

示例插件 plugins/examples/custom-theme 通过 registerAppTheme 注册主题,入口 src/index.tsx 只有两行:

import { registerAppTheme } from '@kinvolk/headlamp-plugin/lib';
import { customTheme, customThemeWithTerminal } from './themes';

registerAppTheme(customTheme);
registerAppTheme(customThemeWithTerminal);

主题对象(AppTheme)的结构在 src/themes.ts 中有完整示例,包含 namebase'light'/'dark')、primarysecondarytextbackgroundsidebar(背景、前景、选中态背景/前景、操作按钮背景)、navbarbuttonTextTransformradius 等字段:

export const customTheme: AppTheme = {
  name: 'my custom theme',
  base: 'light',
  primary: '#414141',
  secondary: '#eff2f5',
  text: { primary: '#44444f' },
  background: { muted: '#f5f5f5' },
  sidebar: {
    background: '#f0f0f0',
    color: '#605e5c',
    selectedBackground: '#f2e600',
    selectedColor: '#292827',
    actionBackground: '#414141',
  },
  navbar: { background: '#f0f0f0', color: '#292827' },
  buttonTextTransform: 'none',
  radius: 6,
};

注册后,用户可在 Settings → General 的下拉框中看到并切换“my custom theme”。示例还演示了可选的 terminal 字段:用于覆盖 xterm.js 终端(Pod 日志查看器、Pod exec 终端、Node shell)的配色,例如在浅色主题内保持深色终端:

export const customThemeWithTerminal: AppTheme = {
  name: 'my custom theme with terminal',
  base: 'light',
  primary: '#414141',
  text: { primary: '#44444f' },
  background: { muted: '#f5f5f5' },
  terminal: {
    background: '#1e1e1e',
    foreground: '#f5f5f5',
    cursor: '#ffcc00',
    ansi: {
      red: '#ff5555',
      green: '#50fa7b',
      yellow: '#f1fa8c',
      blue: '#8be9fd',
      magenta: '#ff79c6',
      cyan: '#8be9fd',
    },
  },
};

主题代码中的注释还给出了可访问性建议:终端 foregroundbackground 之间应保持 4.5:1 的对比度(WCAG 2.1 AA),以保证日志可读。

2. 自定义应用 Logo

典型用途:企业品牌、自定义视觉识别、主题感知的 Logo、响应式 Logo。

示例插件 plugins/examples/change-logo 演示了 registerAppLogo 的三种用法(见 src/index.tsx):

  • 纯文本 Logo:只需两行代码:
import { registerAppLogo } from '@kinvolk/headlamp-plugin/lib';
registerAppLogo(() => <p>My Logo</p>);
  • SVG 响应式 Logo:接收 AppLogoProps(含 logoTypethemeNameclassNamesx)。logoType'small'(移动端)或 'large'(桌面/平板端);themeName'dark''light',据此可返回主题感知的 Logo:
export function ReactiveLogo(props: AppLogoProps) {
  const { logoType, themeName } = props;
  if (logoType === 'small' && themeName === 'dark') return <p>small dark theme logo</p>;
  if (logoType === 'small' && themeName === 'light') return <p>small light theme logo</p>;
  if (logoType === 'large' && themeName === 'dark') return <p>large dark theme logo</p>;
  return <p>large light theme logo</p>;
}
  • 图片 + 文本组合SimpleLogo 在用户通过设置面板配置了 Logo URL 时渲染 Avatar 图片,否则回退到两个 SVG 文件(大图 icon-large-light.svg、小图 icon-small-light.svg):
function SimpleLogo(props: AppLogoProps) {
  const { logoType, className, sx } = props;
  const config = store.useConfig()();
  return config?.url ? (
    <Avatar src={config?.url} alt="logo" className={className} sx={sx} />
  ) : (
    <SvgIcon
      className={className}
      component={logoType === 'large' ? LogoWithTextLight : LogoLight}
      viewBox="0 0 auto 32"
      sx={sx}
    />
  );
}

配合前文提到的 ConfigStoreregisterPluginSettings,用户可以在设置面板中输入 Logo URL 并自动保存,实现“可配置的 Logo”。运行效果见 docs/development/plugins/images/change-logo.png

高级模式

1. 数据可视化(图表)

典型用途:资源用量图表、集群健康仪表盘、性能指标、自定义监控。

示例插件 plugins/examples/resource-charts 在集群总览页(Overview)上新增“Pods Failed”图表,代码见 src/index.tsx。核心是 registerOverviewChartsProcessorTileChart 组件的组合:

function PodFailureChart() {
  const [pods, error] = K8s.ResourceClasses.Pod.useList();
  const theme = useTheme();
  const failedPods = (pods || []).filter(pod => {
    const phase = pod.status?.phase;
    return phase === 'Failed' || phase === 'Unknown';
  });

  if (error) {
    return (
      <Box p={2}>
        <Paper>
          <Box p={2}>{`Error loading pods: ${error}`}</Box>
        </Paper>
      </Box>
    );
  }

  const totalPods = pods?.length || 0;
  const failedCount = failedPods.length;

  return (
    <TileChart
      title="Pods Failed"
      data={[{ name: 'failed', value: failedCount, fill: theme.palette.error.main }]}
      total={totalPods}
      label={totalPods === 0 ? '0' : `${((failedCount / totalPods) * 100).toFixed(1)}%`}
      legend={totalPods === 0 ? 'No pods found' : `${failedCount} failed / ${totalPods} total`}
    />
  );
}

registerOverviewChartsProcessor({
  id: 'pod-failed',
  processor: charts => {
    return [
      ...charts,
      { id: 'pod-failed', component: () => <PodFailureChart /> },
    ];
  },
});

要点:useList() 提供实时数据;错误时渲染错误卡片;TileChart 接收 titledata(含 name/value/fill)、totallabellegend,颜色可直接取自 MUI 主题(theme.palette.error.main),与 Headlamp 视觉体系保持一致。

2. UI 面板(UIPanel)

典型用途:自定义仪表盘、专用视图、可复用 UI 组件、复杂数据展示。

示例插件 plugins/examples/ui-panels 通过 registerUIPanel 在界面的上、下、左、右四个方向各注册一个面板,代码见 src/index.tsx

registerUIPanel({
  id: 'top-panel',
  side: 'top',
  component: () => (
    <div
      role="region"
      aria-label="top panel"
      style={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        height: '50px',
        flexShrink: 0,
        border: '1px solid green',
      }}
    >
      Top Panel
    </div>
  ),
});

registerUIPanel({ id: 'bottom-panel', side: 'bottom', component: () => /* ... */ });
registerUIPanel({ id: 'left-panel',   side: 'left',   component: () => /* ... */ });
registerUIPanel({ id: 'right-panel',  side: 'right',  component: () => /* ... */ });

side 支持 'top' | 'bottom' | 'left' | 'right',组件内可自由组合 MUI 组件或自定义内容,是搭建自定义布局与可复用面板的基础设施。

3. 自定义集群选择器

典型用途:自定义集群发现、增强集群元数据、专用集群视图、多环境管理。

示例插件 plugins/examples/cluster-chooser 同时替换了顶部栏的集群选择器与“无集群”空状态界面,代码见 src/index.tsx

export function ClusterChooserButton({ clickHandler, cluster }: ClusterChooserButtonProps) {
  const clusters = K8s.useClustersConf();
  const clusterNames = clusters ? Object.keys(clusters) : [];

  return (
    <Button onClick={clickHandler}>
      Our Cluster Chooser button. Cluster: {cluster} ({clusterNames.length} clusters)
    </Button>
  );
}

registerClusterChooser(({ clickHandler, cluster }: ClusterChooserProps) => (
  <ClusterChooserButton clickHandler={clickHandler} cluster={cluster} />
));

registerClusterEmptyState(CustomClusterEmptyState);

要点:

  • registerClusterChooser 接收一个渲染函数,props 提供 clickHandler(点击后打开默认集群选择面板)与 cluster(当前集群名);
  • 通过 K8s.useClustersConf() 可读取全部已配置集群,从而展示“当前集群 + 集群总数”;
  • registerClusterEmptyState 用于定制“没有集群”时的引导页,其 props 中的 defaultContent 是默认内容,可在其基础上包裹自定义文案(如“Choose a cluster provider”),实现既保留原生能力又叠加品牌信息。

运行效果见 docs/development/plugins/images/cluster-chooser.png

模式速查与选型建议

目标区域核心 API参考示例(仓库内路径)
顶部导航栏registerAppBarActionplugins/examples/pod-counter
侧边栏 / 路由registerSidebarEntryregisterRouteregisterSidebarEntryFilterregisterRouteFilterplugins/examples/sidebar
资源详情页registerDetailsViewSectionregisterDetailsViewHeaderActionplugins/examples/details-view
资源表格registerResourceTableColumnsProcessorplugins/examples/tables
桌面应用菜单Headlamp.setAppMenuPlugin 基类plugins/examples/app-menus
动态集群Headlamp.setClusterApiProxy.requestplugins/examples/dynamic-clusters
插件设置registerPluginSettingsConfigStoreplugins/examples/pod-counterplugins/examples/change-logo
自定义主题registerAppThemeplugins/examples/custom-theme
自定义 LogoregisterAppLogoplugins/examples/change-logo
图表可视化registerOverviewChartsProcessorTileChartplugins/examples/resource-charts
UI 面板registerUIPanelplugins/examples/ui-panels
集群选择器registerClusterChooserregisterClusterEmptyStateplugins/examples/cluster-chooser

选型建议:需要全局信息或快捷入口时优先考虑 App Bar;需要承载一组独立页面时使用侧边栏 + 路由;对单个资源做增值展示(元数据、操作)时使用详情页扩展;需要行级操作时扩展表格列;品牌化场景则同时考虑自定义主题与 Logo。若需要了解插件的构建、打包与发布,可继续阅读 BuildingPublishing 指南;插件的国际化支持可参考 i18n。上述所有模式均有可运行示例支撑,直接以对应示例为起点进行二次开发,是上手 Headlamp 插件开发最高效的路径。

【免费下载链接】headlamp A Kubernetes web UI that is fully-featured, user-friendly and extensible 【免费下载链接】headlamp 项目地址: https://gitcode.com/GitHub_Trending/he/headlamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值