Skip to content

UI 组件开发

编程式场景支持自定义 React 面板组件,实现丰富的用户界面。

组件注册

第一步:定义面板

getPlugin()ui.panels 中声明面板:

typescript
getPlugin() {
  return {
    ui: {
      layout: 'dashboard-centric',
      panels: [
        {
          id: 'dashboard',
          component: 'DashboardPanel',
          region: 'primary',
          defaultVisible: true,
          resizable: true,
          minWidth: 300,
          maxWidth: 800,
        },
        {
          id: 'settings',
          component: 'SettingsPanel',
          region: 'secondary',
          defaultVisible: false,
          resizable: true,
          minWidth: 250,
          maxWidth: 500,
        },
        {
          id: 'chat',
          component: 'ChatPanel',
          region: 'auxiliary',
          defaultVisible: true,
          resizable: true,
          minWidth: 300,
          maxWidth: 600,
        },
      ],
    },
  }
}

面板区域

区域说明位置
primary主面板中央区域
secondary次面板左侧或右侧
auxiliary辅助面板聊天面板
bottom底部面板终端/输出
overlay浮层面板弹窗覆盖

面板配置

字段类型说明
idstring面板唯一标识
componentstring组件名(对应 getComponents 的 key)
regionstring面板区域
defaultVisibleboolean默认是否可见
resizableboolean是否可调整大小
minWidthnumber最小宽度(px)
maxWidthnumber最大宽度(px)
minHeightnumber最小高度(px)
maxHeightnumber最大高度(px)

第二步:注册组件

getComponents() 中返回组件映射:

typescript
import { DashboardPanel } from './components/DashboardPanel.js'
import { SettingsPanel } from './components/SettingsPanel.js'

getComponents() {
  return {
    DashboardPanel,
    SettingsPanel,
  }
}

组件开发

基础组件

tsx
import React from 'react'

export const DashboardPanel: React.FC = () => {
  return (
    <div className="dashboard-panel">
      <h2>仪表盘</h2>
      <p>欢迎使用场景仪表盘</p>
    </div>
  )
}

使用共享依赖

组件可以使用客户端注入的共享依赖:

tsx
import React, { useState, useEffect } from 'react'
import { BarChart3, TrendingUp } from 'lucide-react'

interface StatsData {
  total: number
  completed: number
  pending: number
}

export const StatsPanel: React.FC = () => {
  const [stats, setStats] = useState<StatsData>({
    total: 0,
    completed: 0,
    pending: 0,
  })

  useEffect(() => {
    // 获取数据
    loadStats()
  }, [])

  const loadStats = async () => {
    // 通过场景数据通道获取数据
    // 具体实现取决于场景的数据源
  }

  return (
    <div className="stats-panel p-4">
      <h3 className="text-lg font-semibold mb-4">统计数据</h3>
      <div className="grid grid-cols-3 gap-4">
        <div className="stat-card">
          <BarChart3 className="w-6 h-6 text-blue-500" />
          <span className="text-2xl font-bold">{stats.total}</span>
          <span className="text-sm text-gray-500">总计</span>
        </div>
        <div className="stat-card">
          <TrendingUp className="w-6 h-6 text-green-500" />
          <span className="text-2xl font-bold">{stats.completed}</span>
          <span className="text-sm text-gray-500">已完成</span>
        </div>
        <div className="stat-card">
          <div className="w-6 h-6 text-yellow-500" />
          <span className="text-2xl font-bold">{stats.pending}</span>
          <span className="text-sm text-gray-500">待处理</span>
        </div>
      </div>
    </div>
  )
}

数据通信

组件通过数据通道与场景通信:

tsx
import React, { useEffect } from 'react'

export const DataPanel: React.FC = () => {
  useEffect(() => {
    // 订阅数据通道
    const handleData = (event: CustomEvent) => {
      console.log('收到数据:', event.detail)
    }

    window.addEventListener('scenario:data-update', handleData as EventListener)
    return () => {
      window.removeEventListener('scenario:data-update', handleData as EventListener)
    }
  }, [])

  return <div>Data Panel</div>
}

样式

组件使用 TailwindCSS 或自定义 CSS:

tsx
// 使用 TailwindCSS
<div className="flex items-center justify-between p-4 bg-white rounded-lg shadow">

// 或使用 CSS Modules
import styles from './DashboardPanel.module.css'
<div className={styles.container}>

最佳实践

  1. 组件轻量:保持组件简洁,避免复杂逻辑
  2. 响应式:使用 TailwindCSS 响应式类
  3. 性能:避免不必要的重渲染
  4. 错误边界:添加错误边界处理异常
  5. 加载状态:处理数据加载中的状态
  6. 空状态:处理无数据的情况

AweeClaw AI 应用构建平台