rwadurian/frontend/mining-admin-web/src/app/(dashboard)/market-maker/page.tsx

972 lines
48 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState } from 'react';
import { QRCodeSVG } from 'qrcode.react';
import { PageHeader } from '@/components/layout/page-header';
import {
useMarketMakerConfig,
useInitializeMarketMaker,
useUpdateMarketMakerConfig,
useDepositCash,
useWithdrawCash,
useDepositShares,
useWithdrawShares,
useBlockchainWithdrawCash,
useBlockchainWithdrawShares,
useStartTaker,
useStopTaker,
useTakeOrder,
useStartMaker,
useStopMaker,
useRefreshOrders,
useCancelAllOrders,
useDepth,
useDepthEnabled,
useSetDepthEnabled,
} from '@/features/market-maker';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Skeleton } from '@/components/ui/skeleton';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Switch } from '@/components/ui/switch';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import {
Play,
Pause,
AlertCircle,
CheckCircle2,
RefreshCw,
Wallet,
TrendingUp,
TrendingDown,
BarChart3,
Zap,
PlusCircle,
MinusCircle,
Copy,
Check,
} from 'lucide-react';
export default function MarketMakerPage() {
const { data: configData, isLoading: configLoading, refetch } = useMarketMakerConfig();
const { data: depthData, isLoading: depthLoading } = useDepth(10);
const { data: depthEnabled, isLoading: depthEnabledLoading } = useDepthEnabled();
const runningStatus = configData?.runningStatus;
const initializeMutation = useInitializeMarketMaker();
const depositCashMutation = useDepositCash();
const withdrawCashMutation = useWithdrawCash();
const depositSharesMutation = useDepositShares();
const withdrawSharesMutation = useWithdrawShares();
const blockchainWithdrawCashMutation = useBlockchainWithdrawCash();
const blockchainWithdrawSharesMutation = useBlockchainWithdrawShares();
const startTakerMutation = useStartTaker();
const stopTakerMutation = useStopTaker();
const takeOrderMutation = useTakeOrder();
const startMakerMutation = useStartMaker();
const stopMakerMutation = useStopMaker();
const refreshOrdersMutation = useRefreshOrders();
const cancelAllOrdersMutation = useCancelAllOrders();
const setDepthEnabledMutation = useSetDepthEnabled();
const updateConfigMutation = useUpdateMarketMakerConfig();
const [initAccountSeq, setInitAccountSeq] = useState('MM001');
const [walletAddressInput, setWalletAddressInput] = useState('');
const [depositCashAmount, setDepositCashAmount] = useState('');
const [withdrawCashAmount, setWithdrawCashAmount] = useState('');
const [depositSharesAmount, setDepositSharesAmount] = useState('');
const [withdrawSharesAmount, setWithdrawSharesAmount] = useState('');
const [copiedAddress, setCopiedAddress] = useState(false);
// 区块链提现
const [blockchainWithdrawCashAddress, setBlockchainWithdrawCashAddress] = useState('');
const [blockchainWithdrawCashAmount, setBlockchainWithdrawCashAmount] = useState('');
const [blockchainWithdrawSharesAddress, setBlockchainWithdrawSharesAddress] = useState('');
const [blockchainWithdrawSharesAmount, setBlockchainWithdrawSharesAmount] = useState('');
const handleCopyAddress = async (address: string) => {
await navigator.clipboard.writeText(address);
setCopiedAddress(true);
setTimeout(() => setCopiedAddress(false), 2000);
};
const config = configData?.config;
const formatNumber = (value: string | undefined, decimals: number = 2) => {
if (!value) return '0';
const num = parseFloat(value);
if (isNaN(num)) return '0';
return num.toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
};
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<PageHeader title="做市商管理" description="管理做市商配置、资金和深度挂单" />
<Button variant="outline" size="sm" onClick={() => refetch()}>
<RefreshCw className="h-4 w-4 mr-2" />
</Button>
</div>
{/* 未初始化状态 */}
{!configLoading && !config && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="space-y-2">
<Label></Label>
<Input
value={initAccountSeq}
onChange={(e) => setInitAccountSeq(e.target.value)}
placeholder="做市商专用账户序列号"
/>
</div>
<Button
onClick={() => initializeMutation.mutate({ accountSequence: initAccountSeq })}
disabled={initializeMutation.isPending || !initAccountSeq}
>
{initializeMutation.isPending ? '初始化中...' : '初始化做市商'}
</Button>
</div>
</CardContent>
</Card>
)}
{configLoading && (
<div className="space-y-6">
<Skeleton className="h-48 w-full" />
<Skeleton className="h-48 w-full" />
</div>
)}
{config && (
<>
{/* 资金状态卡片 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* 现金余额 */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="text-lg flex items-center gap-2">
<Wallet className="h-5 w-5 text-green-500" />
</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold">{formatNumber(config.cashBalance, 2)}</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold text-green-500">{formatNumber(config.availableCash, 2)}</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-lg text-muted-foreground">{formatNumber(config.frozenCash, 2)}</p>
</div>
</div>
<div className="flex gap-2 pt-4 border-t">
<Dialog>
<DialogTrigger asChild>
<Button size="sm" variant="outline">
<PlusCircle className="h-4 w-4 mr-1" />
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<Tabs defaultValue="centralized" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="centralized"></TabsTrigger>
<TabsTrigger value="blockchain"></TabsTrigger>
</TabsList>
<TabsContent value="centralized" className="space-y-4 pt-4">
<div>
<Label></Label>
<Input
type="number"
value={depositCashAmount}
onChange={(e) => setDepositCashAmount(e.target.value)}
placeholder="请输入金额"
/>
</div>
<Button
className="w-full"
onClick={() => {
depositCashMutation.mutate({ amount: depositCashAmount });
setDepositCashAmount('');
}}
disabled={depositCashMutation.isPending || !depositCashAmount}
>
{depositCashMutation.isPending ? '处理中...' : '确认充值'}
</Button>
</TabsContent>
<TabsContent value="blockchain" className="space-y-4 pt-4">
<div className="text-sm text-muted-foreground text-center">
<strong>fUSDT</strong> ()
</div>
{config.kavaWalletAddress ? (
<div className="flex flex-col items-center space-y-4">
<div className="p-4 bg-white rounded-lg">
<QRCodeSVG value={config.kavaWalletAddress} size={180} />
</div>
<div className="w-full">
<Label className="text-xs text-muted-foreground"> (Kava EVM)</Label>
<div className="flex items-center gap-2 mt-1">
<code className="flex-1 text-xs bg-muted p-2 rounded break-all">
{config.kavaWalletAddress}
</code>
<Button
size="sm"
variant="outline"
onClick={() => handleCopyAddress(config.kavaWalletAddress!)}
>
{copiedAddress ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</Button>
</div>
</div>
<div className="text-xs text-yellow-600 bg-yellow-50 p-2 rounded w-full">
<AlertCircle className="h-3 w-3 inline mr-1" />
12
</div>
</div>
) : (
<div className="space-y-4">
<div className="text-center text-muted-foreground py-2">
<AlertCircle className="h-6 w-6 mx-auto mb-2 text-yellow-500" />
<p className="text-sm"></p>
</div>
<div>
<Label className="text-xs"> (Kava EVM)</Label>
<Input
value={walletAddressInput}
onChange={(e) => setWalletAddressInput(e.target.value)}
placeholder="0x..."
className="mt-1"
/>
</div>
<Button
className="w-full"
onClick={() => {
updateConfigMutation.mutate({ kavaWalletAddress: walletAddressInput });
setWalletAddressInput('');
}}
disabled={updateConfigMutation.isPending || !walletAddressInput || !walletAddressInput.startsWith('0x')}
>
{updateConfigMutation.isPending ? '保存中...' : '保存钱包地址'}
</Button>
</div>
)}
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger asChild>
<Button size="sm" variant="outline">
<MinusCircle className="h-4 w-4 mr-1" />
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<Tabs defaultValue="centralized" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="centralized"></TabsTrigger>
<TabsTrigger value="blockchain"></TabsTrigger>
</TabsList>
<TabsContent value="centralized" className="space-y-4 pt-4">
<div>
<Label></Label>
<Input
type="number"
value={withdrawCashAmount}
onChange={(e) => setWithdrawCashAmount(e.target.value)}
placeholder="请输入金额"
/>
</div>
<Button
className="w-full"
onClick={() => {
withdrawCashMutation.mutate({ amount: withdrawCashAmount });
setWithdrawCashAmount('');
}}
disabled={withdrawCashMutation.isPending || !withdrawCashAmount}
>
{withdrawCashMutation.isPending ? '处理中...' : '确认提现'}
</Button>
</TabsContent>
<TabsContent value="blockchain" className="space-y-4 pt-4">
<div className="text-sm text-muted-foreground text-center">
<strong>fUSDT</strong>
</div>
<div>
<Label> (Kava EVM)</Label>
<Input
value={blockchainWithdrawCashAddress}
onChange={(e) => setBlockchainWithdrawCashAddress(e.target.value)}
placeholder="0x..."
/>
</div>
<div>
<Label></Label>
<Input
type="number"
value={blockchainWithdrawCashAmount}
onChange={(e) => setBlockchainWithdrawCashAmount(e.target.value)}
placeholder="请输入金额"
/>
</div>
<Button
className="w-full"
onClick={() => {
blockchainWithdrawCashMutation.mutate({
toAddress: blockchainWithdrawCashAddress,
amount: blockchainWithdrawCashAmount,
});
setBlockchainWithdrawCashAddress('');
setBlockchainWithdrawCashAmount('');
}}
disabled={
blockchainWithdrawCashMutation.isPending ||
!blockchainWithdrawCashAddress ||
!blockchainWithdrawCashAmount
}
>
{blockchainWithdrawCashMutation.isPending ? '链上转账中...' : '确认区块链提现'}
</Button>
<div className="text-xs text-yellow-600 bg-yellow-50 p-2 rounded">
<AlertCircle className="h-3 w-3 inline mr-1" />
</div>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
</div>
</div>
</CardContent>
</Card>
{/* 积分股余额 */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="text-lg flex items-center gap-2">
<BarChart3 className="h-5 w-5 text-blue-500" />
</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold">{formatNumber(config.shareBalance, 2)}</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold text-blue-500">{formatNumber(config.availableShares, 2)}</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-lg text-muted-foreground">{formatNumber(config.frozenShares, 2)}</p>
</div>
</div>
<div className="flex gap-2 pt-4 border-t">
<Dialog>
<DialogTrigger asChild>
<Button size="sm" variant="outline">
<PlusCircle className="h-4 w-4 mr-1" />
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<Tabs defaultValue="centralized" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="centralized"></TabsTrigger>
<TabsTrigger value="blockchain"></TabsTrigger>
</TabsList>
<TabsContent value="centralized" className="space-y-4 pt-4">
<div>
<Label></Label>
<Input
type="number"
value={depositSharesAmount}
onChange={(e) => setDepositSharesAmount(e.target.value)}
placeholder="请输入数量"
/>
</div>
<Button
className="w-full"
onClick={() => {
depositSharesMutation.mutate({ amount: depositSharesAmount });
setDepositSharesAmount('');
}}
disabled={depositSharesMutation.isPending || !depositSharesAmount}
>
{depositSharesMutation.isPending ? '处理中...' : '确认充值'}
</Button>
</TabsContent>
<TabsContent value="blockchain" className="space-y-4 pt-4">
<div className="text-sm text-muted-foreground text-center">
<strong>eUSDT</strong> ()
</div>
{config.kavaWalletAddress ? (
<div className="flex flex-col items-center space-y-4">
<div className="p-4 bg-white rounded-lg">
<QRCodeSVG value={config.kavaWalletAddress} size={180} />
</div>
<div className="w-full">
<Label className="text-xs text-muted-foreground"> (Kava EVM)</Label>
<div className="flex items-center gap-2 mt-1">
<code className="flex-1 text-xs bg-muted p-2 rounded break-all">
{config.kavaWalletAddress}
</code>
<Button
size="sm"
variant="outline"
onClick={() => handleCopyAddress(config.kavaWalletAddress!)}
>
{copiedAddress ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</Button>
</div>
</div>
<div className="text-xs text-yellow-600 bg-yellow-50 p-2 rounded w-full">
<AlertCircle className="h-3 w-3 inline mr-1" />
12
</div>
</div>
) : (
<div className="space-y-4">
<div className="text-center text-muted-foreground py-2">
<AlertCircle className="h-6 w-6 mx-auto mb-2 text-yellow-500" />
<p className="text-sm"></p>
</div>
<div>
<Label className="text-xs"> (Kava EVM)</Label>
<Input
value={walletAddressInput}
onChange={(e) => setWalletAddressInput(e.target.value)}
placeholder="0x..."
className="mt-1"
/>
</div>
<Button
className="w-full"
onClick={() => {
updateConfigMutation.mutate({ kavaWalletAddress: walletAddressInput });
setWalletAddressInput('');
}}
disabled={updateConfigMutation.isPending || !walletAddressInput || !walletAddressInput.startsWith('0x')}
>
{updateConfigMutation.isPending ? '保存中...' : '保存钱包地址'}
</Button>
</div>
)}
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger asChild>
<Button size="sm" variant="outline">
<MinusCircle className="h-4 w-4 mr-1" />
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<Tabs defaultValue="centralized" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="centralized"></TabsTrigger>
<TabsTrigger value="blockchain"></TabsTrigger>
</TabsList>
<TabsContent value="centralized" className="space-y-4 pt-4">
<div>
<Label></Label>
<Input
type="number"
value={withdrawSharesAmount}
onChange={(e) => setWithdrawSharesAmount(e.target.value)}
placeholder="请输入数量"
/>
</div>
<Button
className="w-full"
onClick={() => {
withdrawSharesMutation.mutate({ amount: withdrawSharesAmount });
setWithdrawSharesAmount('');
}}
disabled={withdrawSharesMutation.isPending || !withdrawSharesAmount}
>
{withdrawSharesMutation.isPending ? '处理中...' : '确认提取'}
</Button>
</TabsContent>
<TabsContent value="blockchain" className="space-y-4 pt-4">
<div className="text-sm text-muted-foreground text-center">
<strong>eUSDT</strong>
</div>
<div>
<Label> (Kava EVM)</Label>
<Input
value={blockchainWithdrawSharesAddress}
onChange={(e) => setBlockchainWithdrawSharesAddress(e.target.value)}
placeholder="0x..."
/>
</div>
<div>
<Label></Label>
<Input
type="number"
value={blockchainWithdrawSharesAmount}
onChange={(e) => setBlockchainWithdrawSharesAmount(e.target.value)}
placeholder="请输入数量"
/>
</div>
<Button
className="w-full"
onClick={() => {
blockchainWithdrawSharesMutation.mutate({
toAddress: blockchainWithdrawSharesAddress,
amount: blockchainWithdrawSharesAmount,
});
setBlockchainWithdrawSharesAddress('');
setBlockchainWithdrawSharesAmount('');
}}
disabled={
blockchainWithdrawSharesMutation.isPending ||
!blockchainWithdrawSharesAddress ||
!blockchainWithdrawSharesAmount
}
>
{blockchainWithdrawSharesMutation.isPending ? '链上转账中...' : '确认区块链提现'}
</Button>
<div className="text-xs text-yellow-600 bg-yellow-50 p-2 rounded">
<AlertCircle className="h-3 w-3 inline mr-1" />
</div>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
</div>
</div>
</CardContent>
</Card>
</div>
{/* 运行模式状态 */}
<Card className="border-2 border-primary/20">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-lg"></CardTitle>
{runningStatus?.mode === 'idle' && (
<Badge variant="secondary" className="text-base px-3 py-1">
<Pause className="h-4 w-4 mr-1" />
</Badge>
)}
{runningStatus?.mode === 'taker' && (
<Badge variant="default" className="bg-green-500 text-base px-3 py-1">
<Zap className="h-4 w-4 mr-1" />
</Badge>
)}
{runningStatus?.mode === 'maker' && (
<Badge variant="default" className="bg-blue-500 text-base px-3 py-1">
<BarChart3 className="h-4 w-4 mr-1" />
</Badge>
)}
</div>
</CardHeader>
<CardContent>
<div className="flex items-center gap-6 text-sm text-muted-foreground">
<div className="flex items-center gap-2">
<span className={`w-2 h-2 rounded-full ${runningStatus?.takerRunning ? 'bg-green-500' : 'bg-gray-300'}`} />
<span>: {runningStatus?.takerRunning ? '运行中' : '未运行'}</span>
</div>
<div className="flex items-center gap-2">
<span className={`w-2 h-2 rounded-full ${runningStatus?.makerRunning ? 'bg-blue-500' : 'bg-gray-300'}`} />
<span>: {runningStatus?.makerRunning ? '运行中' : '未运行'}</span>
</div>
<div className="ml-auto text-xs bg-yellow-100 text-yellow-800 px-2 py-1 rounded">
<AlertCircle className="h-3 w-3 inline mr-1" />
</div>
</div>
</CardContent>
</Card>
{/* 运行模式控制 */}
<Tabs defaultValue="taker">
<TabsList>
<TabsTrigger value="taker"></TabsTrigger>
<TabsTrigger value="maker"></TabsTrigger>
<TabsTrigger value="depth"></TabsTrigger>
</TabsList>
{/* 吃单模式 */}
<TabsContent value="taker">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg flex items-center gap-2">
<Zap className="h-5 w-5" />
</CardTitle>
<CardDescription>
Taker
</CardDescription>
</div>
{runningStatus?.takerRunning ? (
<Badge variant="default" className="bg-green-500">
<CheckCircle2 className="h-3 w-3 mr-1" />
</Badge>
) : (
<Badge variant="secondary">
<Pause className="h-3 w-3 mr-1" />
</Badge>
)}
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{formatNumber(String(parseFloat(config.maxBuyRatio) * 100), 2)}%</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{config.minIntervalMs}ms</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{config.maxIntervalMs}ms</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{config.priceStrategy}</p>
</div>
</div>
<div className="flex gap-2 pt-4 border-t">
{runningStatus?.takerRunning ? (
<Button
variant="destructive"
onClick={() => stopTakerMutation.mutate()}
disabled={stopTakerMutation.isPending}
>
<Pause className="h-4 w-4 mr-2" />
{stopTakerMutation.isPending ? '停止中...' : '停止吃单'}
</Button>
) : (
<Button
onClick={() => startTakerMutation.mutate()}
disabled={startTakerMutation.isPending}
>
<Play className="h-4 w-4 mr-2" />
{startTakerMutation.isPending ? '启动中...' : '启动吃单'}
</Button>
)}
<Button
variant="outline"
onClick={() => takeOrderMutation.mutate()}
disabled={takeOrderMutation.isPending}
>
<Zap className="h-4 w-4 mr-2" />
{takeOrderMutation.isPending ? '执行中...' : '手动吃单'}
</Button>
</div>
{runningStatus?.makerRunning && (
<div className="text-sm text-yellow-600 bg-yellow-50 p-3 rounded">
<AlertCircle className="h-4 w-4 inline mr-1" />
</div>
)}
</div>
</CardContent>
</Card>
</TabsContent>
{/* 挂单模式 */}
<TabsContent value="maker">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg flex items-center gap-2">
<BarChart3 className="h-5 w-5" />
</CardTitle>
<CardDescription>
Maker
</CardDescription>
</div>
{runningStatus?.makerRunning ? (
<Badge variant="default" className="bg-blue-500">
<CheckCircle2 className="h-3 w-3 mr-1" />
</Badge>
) : (
<Badge variant="secondary">
<Pause className="h-3 w-3 mr-1" />
</Badge>
)}
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
{/* 警告提示 */}
<div className="text-sm text-orange-600 bg-orange-50 p-3 rounded border border-orange-200">
<AlertCircle className="h-4 w-4 inline mr-1" />
<strong></strong>
使
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{config.bidLevels || 5}</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{formatNumber(String(parseFloat(config.bidSpread || '0.01') * 100), 2)}%</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{formatNumber(config.bidQuantityPerLevel || '1000', 0)}</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{config.askLevels || 5}</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{formatNumber(String(parseFloat(config.askSpread || '0.01') * 100), 2)}%</p>
</div>
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="font-semibold">{formatNumber(config.askQuantityPerLevel || '1000', 0)}</p>
</div>
</div>
<div className="flex gap-2 pt-4 border-t">
{runningStatus?.makerRunning ? (
<Button
variant="destructive"
onClick={() => stopMakerMutation.mutate()}
disabled={stopMakerMutation.isPending}
>
<Pause className="h-4 w-4 mr-2" />
{stopMakerMutation.isPending ? '停止中...' : '停止挂单'}
</Button>
) : (
<Button
variant="outline"
onClick={() => startMakerMutation.mutate()}
disabled={startMakerMutation.isPending}
>
<Play className="h-4 w-4 mr-2" />
{startMakerMutation.isPending ? '启动中...' : '启动挂单'}
</Button>
)}
<Button
variant="outline"
onClick={() => refreshOrdersMutation.mutate()}
disabled={refreshOrdersMutation.isPending || !runningStatus?.makerRunning}
>
<RefreshCw className="h-4 w-4 mr-2" />
{refreshOrdersMutation.isPending ? '刷新中...' : '刷新挂单'}
</Button>
<Button
variant="outline"
onClick={() => cancelAllOrdersMutation.mutate()}
disabled={cancelAllOrdersMutation.isPending}
>
{cancelAllOrdersMutation.isPending ? '取消中...' : '取消所有挂单'}
</Button>
</div>
{runningStatus?.takerRunning && (
<div className="text-sm text-yellow-600 bg-yellow-50 p-3 rounded">
<AlertCircle className="h-4 w-4 inline mr-1" />
</div>
)}
</div>
</CardContent>
</Card>
</TabsContent>
{/* 深度显示控制 */}
<TabsContent value="depth">
<div className="space-y-6">
{/* 深度开关 */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg"></CardTitle>
<CardDescription>App端是否显示买卖深度</CardDescription>
</div>
{depthEnabledLoading ? (
<Skeleton className="h-6 w-16" />
) : depthEnabled?.enabled ? (
<Badge variant="default" className="bg-green-500">
<CheckCircle2 className="h-3 w-3 mr-1" />
</Badge>
) : (
<Badge variant="secondary">
<Pause className="h-3 w-3 mr-1" />
</Badge>
)}
</div>
</CardHeader>
<CardContent>
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
<div>
<p className="font-medium"></p>
<p className="text-sm text-muted-foreground">
{depthEnabled?.enabled ? '用户可以在App中查看买卖深度' : '深度功能已关闭,用户无法查看'}
</p>
</div>
<Switch
checked={depthEnabled?.enabled ?? false}
onCheckedChange={(checked) => setDepthEnabledMutation.mutate(checked)}
disabled={setDepthEnabledMutation.isPending || depthEnabledLoading}
/>
</div>
</CardContent>
</Card>
{/* 当前深度 */}
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent>
{depthLoading ? (
<Skeleton className="h-64 w-full" />
) : (
<div className="grid grid-cols-2 gap-6">
{/* 买单深度 */}
<div>
<h4 className="font-medium text-green-500 mb-2 flex items-center gap-1">
<TrendingUp className="h-4 w-4" />
</h4>
{depthData?.bids?.length ? (
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{depthData.bids.map((bid, idx) => (
<TableRow key={idx}>
<TableCell className="font-mono text-green-500">{formatNumber(bid.price, 8)}</TableCell>
<TableCell className="font-mono">{formatNumber(bid.quantity, 2)}</TableCell>
<TableCell className="font-mono text-muted-foreground">{formatNumber(bid.total, 2)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
) : (
<p className="text-center text-muted-foreground py-4"></p>
)}
</div>
{/* 卖单深度 */}
<div>
<h4 className="font-medium text-red-500 mb-2 flex items-center gap-1">
<TrendingDown className="h-4 w-4" />
</h4>
{depthData?.asks?.length ? (
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{depthData.asks.map((ask, idx) => (
<TableRow key={idx}>
<TableCell className="font-mono text-red-500">{formatNumber(ask.price, 8)}</TableCell>
<TableCell className="font-mono">{formatNumber(ask.quantity, 2)}</TableCell>
<TableCell className="font-mono text-muted-foreground">{formatNumber(ask.total, 2)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
) : (
<p className="text-center text-muted-foreground py-4"></p>
)}
</div>
</div>
)}
</CardContent>
</Card>
</div>
</TabsContent>
</Tabs>
</>
)}
</div>
);
}