Orderly Ui Components

基于 React 的预构建交易界面组件,支持快速搭建去中心化交易平台。

已扫描
适合谁
Web3 金融产品开发者、去中心化交易所(DEX)UI 设计师
不适合谁
无前端开发经验的用户、非区块链或金融类应用开发者
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @tarnadas/orderly-ui-components

Skill 说明

命令、参数、文件名以原文为准

Orderly 网络:UI 组件

本技能涵盖使用 @orderly.network/react 提供的预构建 React 组件来构建交易界面。

适用场景

  • 快速构建交易界面
  • 使用预构建、已样式化的组件
  • 实现标准的交易界面模式

前置条件

  • React 18+
  • 已安装 @orderly.network/react
  • Tailwind CSS(推荐用于样式)

安装

npm install @orderly.network/react @orderly.network/hooks @orderly.network/types

# 或使用 yarn
yarn add @orderly.network/react @orderly.network/hooks @orderly.network/types

核心提供者

在应用根部包裹所需的提供者:

import {
  OrderlyAppProvider,
  TradingPageProvider,
  SymbolProvider,
  WalletConnector
} from '@orderly.network/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <OrderlyAppProvider
        brokerId="woofi_dex"
        chainFilter={[42161, 421614]}
      >
        <SymbolProvider>
          <TradingPageProvider>
            <Layout>
              <WalletConnector />
              <TradingPage />
            </Layout>
          </TradingPageProvider>
        </SymbolProvider>
      </OrderlyAppProvider>
    </QueryClientProvider>
  );
}

订单输入组件

基础订单输入

import { OrderEntry, OrderEntryProvider } from '@orderly.network/ui-order-entry';
import { useOrderEntry } from '@orderly.network/hooks';

function OrderEntryContainer() {
  const { onSubmit } = useOrderEntry();

  const handleSubmit = async (params: any) => {
    try {
      await onSubmit(params);
      console.log('订单提交成功');
    } catch (e) {
      console.error('订单提交失败', e);
    }
  };

  return (
    <div className="bg-gray-900 rounded-lg p-4 border border-gray-800">
      <OrderEntry
        onSubmit={handleSubmit}
        defaultTab="limit"
        hideMarket={false}
      />
    </div>
  );
}

export function OrderEntryWidget() {
  return (
    <OrderEntryProvider symbol="PERP_BTC_USDC">
      <OrderEntryContainer />
    </OrderEntryProvider>
  );
}

订单输入属性

interface OrderEntryProps {
  onSubmit?: (params: OrderParams) => Promise<void>;
  defaultTab?: 'limit' | 'market';
  hideMarket?: boolean;
  hideLimit?: boolean;
  showLeverage?: boolean;
  className?: string;
}

持仓组件

基础持仓表格

import { Positions } from '@orderly.network/ui-positions';

export function PositionsWidget() {
  const handleSymbolClick = (symbol: string) => {
    console.log('跳转至:', symbol);
  };

  return (
    <div className="w-full">
      <Positions
        filter={{ symbol: 'PERP_ETH_USDC' }}  // 可选筛选条件
        onSymbolClick={handleSymbolClick}
        showPagination={true}
      />
    </div>
  );
}

持仓属性

interface PositionsProps {
  filter?: {
    symbol?: string;
    side?: 'BUY' | 'SELL';
  };
  onSymbolClick?: (symbol: string) => void;
  showPagination?: boolean;
  pageSize?: number;
  className?: string;
}

订单簿组件

import { Orderbook, OrderbookProvider } from '@orderly.network/ui-orderbook';

export function OrderbookWidget({ symbol }: { symbol: string }) {
  return (
    <OrderbookProvider symbol={symbol}>
      <Orderbook
        level={10}           // 显示的层级数量
        showTotal={true}     // 是否显示总计列
        onItemClick={(price, side) => {
          console.log('价格被点击:', price, side);
        }}
      />
    </OrderbookProvider>
  );
}

钱包连接组件

基础钱包连接

import { WalletConnect } from '@orderly.network/react';

export function Header() {
  return (
    <header className="flex justify-between items-center p-4">
      <div className="logo">我的 DEX</div>
      <WalletConnect />
    </header>
  );
}

自定义钱包按钮

import { useAccount, useWalletConnector } from '@orderly.network/hooks';

export function CustomWalletButton() {
  const { account, state } = useAccount();
  const wallet = useWalletConnector();

  if (state.status !== 'connected') {
    return (
      <button
        onClick={() => wallet.connect()}
        className="btn-primary"
      >
        连接钱包
      </button>
    );
  }

  return (
    <div className="wallet-menu">
      <span>{account.address?.slice(0, 6)}...{account.address?.slice(-4)}</span>
      <button onClick={() => wallet.disconnect()}>
        断开连接
      </button>
    </div>
  );
}

图表组件

TradingView 图表

import { TradingView } from '@orderly.network/ui-chart';

export function ChartWidget({ symbol }: { symbol: string }) {
  return (
    <div className="h-[500px]">
      <TradingView
        symbol={symbol}
        interval="1h"
        theme="dark"
        autosize={true}
      />
    </div>
  );
}

轻量级图表

import { LightweightChart } from '@orderly.network/ui-chart';

export function SimpleChart({ symbol }: { symbol: string }) {
  return (
    <div className="h-[400px]">
      <LightweightChart
        symbol={symbol}
        interval="15m"
        chartType="candlestick"
      />
    </div>
  );
}

数据表格组件

通用表格

import { Table } from '@orderly.network/ui';

type TradeRow = {
  id: string;
  price: number;
  size: number;
  time: string;
  side: 'BUY' | 'SELL';
};

export function TradesTable({ data }: { data: TradeRow[] }) {
  return (
    <Table<TradeRow>
      dataSource={data}
      columns={[
        {
          title: '价格',
          dataIndex: 'price',
          render: (value, record) => (
            <span className={record.side === 'BUY' ? 'text-green-500' : 'text-red-500'}>
              {value.toFixed(2)}
            </span>
          ),
        },
        {
          title: '数量',
          dataIndex: 'size',
          render: (value) => value.toFixed(4),
        },
        {
          title: '时间',
          dataIndex: 'time',
        },
      ]}
      rowKey="id"
      pagination={{ pageSize: 20 }}
    />
  );
}

Sheet(抽屉)组件

import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetFooter } from '@orderly.network/ui';

export function OrderDetailsSheet({ order }: { order: Order }) {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <button className="btn-secondary">查看详情</button>
      </SheetTrigger>
      <SheetContent side="right">
        <SheetHeader>
          <h2>订单详情</h2>
        </SheetHeader>
        <div className="py-4">
          <div className="grid grid-cols-2 gap-2">
            <span>合约:</span>
            <span>{order.symbol}</span>
            <span>方向:</span>
            <span>{order.side}</span>
            <span>价格:</span>
            <span>{order.price}</span>
            <span>数量:</span>
            <span>{order.order_qty}</span>
            <span>状态:</span>
            <span>{order.status}</span>
          </div>
        </div>
        <SheetFooter>
          <button className="btn-primary">平仓</button>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

模态框组件

import { Modal, ModalTrigger, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@orderly.network/ui';

export function ConfirmOrderModal({ order, onConfirm }: { order: Order; onConfirm: () => void }) {
  return (
    <Modal>
      <ModalTrigger asChild>
        <button className="btn-primary">下单</button>
      </ModalTrigger>
      <ModalContent>
        <ModalHeader>
          <h2>确认订单</h2>
        </ModalHeader>
        <ModalBody>
          <p>确定要提交此订单吗?</p>
          <div className="order-summary">
            <p>{order.side} {order.quantity} {order.symbol} @ {order.price}</p>
          </div>
        </ModalBody>
        <ModalFooter>
          <button className="btn-secondary">取消</button>
          <button className="btn-primary" onClick={onConfirm}>确认</button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  );
}

空状态组件

import { NoData } from '@orderly.network/ui';

export function EmptyOrders() {
  return (
    <div className="flex flex-col items-center justify-center py-12">
      <NoData width={200} height={200} className="text-gray-500" />
      <p className="mt-4 text-gray-400">暂无未成交订单</p>
      <button className="mt-2 btn-secondary">下单</button>
    </div>
  );
}

通知/提示组件

import { Toast, ToastProvider, useToast } from '@orderly.network/ui';

function TradingPage() {
  const { toast } = useToast();

  const handleOrderSubmit = async () => {
    try {
      await submitOrder();
      toast({
        title: '订单已提交',
        description: '您的订单已成功提交',
        variant: 'success',
      });
    } catch (error) {
      toast({
        title: '订单提交失败',
        description: error.message,
        variant: 'error',
      });
    }
  };

  return <button onClick={handleOrderSubmit}>下单</button>;
}

// 在应用根部
function App() {
  return (
    <ToastProvider>
      <TradingPage />
    </ToastProvider>
  );
}

完整交易页面示例

import {
  OrderlyAppProvider,
  TradingPageProvider,
  SymbolProvider,
  WalletConnect,
} from '@orderly.network/react';
import { OrderEntry, OrderEntryProvider } from '@orderly.network/ui-order-entry';
import { Positions } from '@orderly.network/ui-positions';
import { Orderbook, OrderbookProvider } from '@orderly.network/ui-orderbook';
import { TradingView } from '@orderly.network/ui-chart';
import { useOrderEntry } from '@orderly.network/hooks';

function TradingPage() {
  const symbol = 'PERP_BTC_USDC';

  return (
    <div className="trading-layout">
      {/* 头部 */}
      <header className="flex justify-between items-center p-4 border-b">
        <h1>我的去中心化交易所</h1>
        <WalletConnect />
      </header>

      {/* 主体内容 */}
      <div className="grid grid-cols-12 gap-4 p-4">
        {/* 左侧:盘口 */}
        <div className="col-span-2">
          <OrderbookProvider symbol={symbol}>
            <Orderbook level={15} />
          </OrderbookProvider>
        </div>

        {/* 中心:图表 + 下单区 */}
        <div className="col-span-7">
          <div className="h-[500px] mb-4">
            <TradingView symbol={symbol} />
          </div>
          <OrderEntryProvider symbol={symbol}>
            <OrderEntry defaultTab="limit" />
          </OrderEntryProvider>
        </div>

        {/* 右侧:持仓 */}
        <div className="col-span-3">
          <Positions showPagination={false} />
        </div>
      </div>
    </div>
  );
}

export function App() {
  return (
    <OrderlyAppProvider brokerId="woofi_dex">
      <SymbolProvider>
        <TradingPageProvider>
          <TradingPage />
        </TradingPageProvider>
      </SymbolProvider>
    </OrderlyAppProvider>
  );
}

样式

Orderly 组件使用 Tailwind CSS 类名。可通过以下方式自定义样式:

/* 覆盖组件样式 */
.order-entry-root {
  /* 自定义样式 */
}

.positions-table {
  /* 自定义样式 */
}

/* 使用 Tailwind 的暗色模式 */
.dark .order-entry-root {
  /* 暗色模式下的样式 */
}

常见问题

组件未渲染

  • 确保所有提供者(Provider)正确嵌套
  • 检查 SymbolProvider 是否位于依赖符号的组件之上
  • 验证 Tailwind CSS 是否已正确配置

样式冲突

  • 组件使用了 Tailwind 的实用类(utility classes)
  • 可通过更高优先级的 CSS 进行覆盖
  • 如有 className 属性可用,建议使用该属性进行自定义样式

数据未更新

  • 检查 WebSocket 连接状态
  • 确认账户已成功连接
  • 确保钩子函数(hooks)在提供者组件内部调用

相关技能

  • orderly-sdk-react-hooks - 钩子函数参考
  • orderly-trading-orders - 订单管理
  • orderly-websocket-streaming - 实时数据流
T
@tarnadas

已收录 1 个 Skill

相关推荐