import { useEffect, useState } from 'react' import { Table, Tag, Select, Input, Button, Card, Divider, Spin, message, Switch, Space } from 'antd' import { apiService } from '../../services/api' import { formatUSDC, isAutoGeneratedOrderId, copyToClipboard, getPolymarketUrl } from '../../utils' import { useMediaQuery } from 'react-responsive' import { useTranslation } from 'react-i18next' import type { SellOrderInfo, OrderTrackingRequest, OrderTrackingListResponse, MarketGroupedOrdersRequest, MarketGroupedOrdersResponse, MarketOrderGroup } from '../../types' const { Option } = Select import { ReloadOutlined, CopyOutlined, DownOutlined, UpOutlined, AppstoreOutlined, UnorderedListOutlined } from '@ant-design/icons' interface SellOrdersTabProps { copyTradingId: string active?: boolean } const SellOrdersTab: React.FC = ({ copyTradingId, active = false }) => { const { t } = useTranslation() const isMobile = useMediaQuery({ maxWidth: 768 }) const [loading, setLoading] = useState(false) const [orders, setOrders] = useState([]) const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const [limit, setLimit] = useState(20) const [filters, setFilters] = useState<{ marketId?: string marketTitle?: string status?: string }>({}) // 从 localStorage 读取用户分组偏好,如果没有则默认为 false const [groupByMarket, setGroupByMarket] = useState(() => { const savedPreference = localStorage.getItem('copyTradingOrders_groupByMarket_sell') return savedPreference === 'true' }) const [expandedMarkets, setExpandedMarkets] = useState>(new Set()) // 展开的市场ID集合 const [groupedOrders, setGroupedOrders] = useState([]) // 分组数据(从后端获取) const [groupedTotal, setGroupedTotal] = useState(0) // 分组总数(市场数量) const handleMarketTitleChange = (value: string) => { setFilters({ ...filters, marketTitle: value || undefined }) } // 切换分组模式时,保存用户偏好到 localStorage const handleToggleGroupByMarket = (checked: boolean) => { setGroupByMarket(checked) localStorage.setItem('copyTradingOrders_groupByMarket_sell', String(checked)) } useEffect(() => { if (copyTradingId && active) { fetchOrders() } }, [copyTradingId, active, page, limit, filters.marketId, filters.status, groupByMarket]) // 防抖搜索 - marketTitle 变化时延迟0.5秒再搜索 useEffect(() => { if (!copyTradingId || !active) return const timer = setTimeout(() => { fetchOrders() }, 500) // 0.5秒延迟 return () => clearTimeout(timer) }, [filters.marketTitle]) const fetchOrders = async () => { if (!copyTradingId) return setLoading(true) try { if (groupByMarket) { // 调用分组接口 const request: MarketGroupedOrdersRequest = { copyTradingId: parseInt(copyTradingId), type: 'sell', page, limit, marketId: filters.marketId, marketTitle: filters.marketTitle } const response = await apiService.orderTracking.listGroupedByMarket(request) if (response.data.code === 0 && response.data.data) { const data = response.data.data as MarketGroupedOrdersResponse setGroupedOrders(data.list || []) setGroupedTotal(data.total || 0) } } else { // 调用普通列表接口 const request: OrderTrackingRequest = { copyTradingId: parseInt(copyTradingId), type: 'sell', page, limit, ...filters } const response = await apiService.orderTracking.list(request) if (response.data.code === 0 && response.data.data) { const data = response.data.data as OrderTrackingListResponse setOrders((data.list || []) as SellOrderInfo[]) setTotal(data.total || 0) } } } catch (error: any) { console.error('获取卖出订单列表失败:', error) } finally { setLoading(false) } } const getPnlColor = (value: string): string => { const num = parseFloat(value) if (isNaN(num)) return '#666' return num >= 0 ? '#3f8600' : '#cf1322' } const handleCopyOrderId = async (orderId: string) => { const success = await copyToClipboard(orderId) if (success) { message.success(t('common.copySuccess') || '已复制到剪贴板') } else { message.error(t('common.copyFailed') || '复制失败') } } // 切换市场展开/折叠 const toggleMarket = (marketId: string) => { const newExpanded = new Set(expandedMarkets) if (newExpanded.has(marketId)) { newExpanded.delete(marketId) } else { newExpanded.add(marketId) } setExpandedMarkets(newExpanded) } // 展开/折叠全部 const toggleAllMarkets = () => { if (expandedMarkets.size === groupedOrders.length) { setExpandedMarkets(new Set()) } else { setExpandedMarkets(new Set(groupedOrders.map(g => g.marketId))) } } // 分组默认全部折叠(不自动展开) const columns = [ { title: t('copyTradingOrders.orderId') || '订单ID', dataIndex: 'orderId', key: 'orderId', width: isMobile ? 120 : 180, render: (text: string) => { const isAuto = isAutoGeneratedOrderId(text) return (
{isMobile ? `${text.slice(0, 6)}...${text.slice(-4)}` : `${text.slice(0, 8)}...${text.slice(-6)}` } {!isAuto && (
) } }, { title: t('copyTradingOrders.leaderTradeId') || 'Leader 交易ID', dataIndex: 'leaderTradeId', key: 'leaderTradeId', width: isMobile ? 100 : 150, render: (text: string) => ( {isMobile ? `${text.slice(0, 6)}...${text.slice(-4)}` : `${text.slice(0, 8)}...${text.slice(-6)}` } ) }, { title: t('copyTradingOrders.market') || '市场', dataIndex: 'marketId', key: 'marketId', width: isMobile ? 120 : 200, render: (text: string, record: SellOrderInfo) => { const marketUrl = getPolymarketUrl(record.marketSlug, record.eventSlug, record.marketCategory, record.marketId) return (
{record.marketTitle ? ( marketUrl ? ( {record.marketTitle} ) : ( {record.marketTitle} ) ) : null} {isMobile ? `${text.slice(0, 6)}...${text.slice(-4)}` : `${text.slice(0, 8)}...${text.slice(-6)}` }
) } }, { title: t('copyTradingOrders.sellQuantity') || '卖出数量', dataIndex: 'quantity', key: 'quantity', width: isMobile ? 80 : 100, render: (value: string) => ( {formatUSDC(value)} ) }, { title: t('copyTradingOrders.sellPrice') || '卖出价格', dataIndex: 'price', key: 'price', width: isMobile ? 80 : 100, render: (value: string) => ( {formatUSDC(value)} ) }, { title: t('copyTradingOrders.sellAmount') || '卖出金额', key: 'amount', width: isMobile ? 100 : 120, render: (_: any, record: SellOrderInfo) => { const amount = (parseFloat(record.quantity) * parseFloat(record.price)).toString() return ( {isMobile ? formatUSDC(amount) : `${formatUSDC(amount)} USDC`} ) } }, { title: t('copyTradingOrders.realizedPnl') || '已实现盈亏', dataIndex: 'realizedPnl', key: 'realizedPnl', width: isMobile ? 100 : 120, render: (value: string) => ( {isMobile ? formatUSDC(value) : `${formatUSDC(value)} USDC`} ) }, { title: t('copyTradingOrders.createdAt') || '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: isMobile ? 120 : 160, render: (timestamp: number) => ( {isMobile ? new Date(timestamp).toLocaleDateString('zh-CN') : new Date(timestamp).toLocaleString('zh-CN') } ) } ] // 渲染分组视图 const renderGroupedView = () => { if (groupedOrders.length === 0) { return (
{t('copyTradingOrders.noSellOrders') || '暂无卖出订单'}
) } return (
{groupedOrders.map((group) => { const isExpanded = expandedMarkets.has(group.marketId) const marketDisplayName = group.marketTitle || group.marketId.slice(0, 8) + '...' + group.marketId.slice(-6) const marketUrl = getPolymarketUrl(group.marketSlug, group.eventSlug, group.marketCategory, group.marketId) const pnlColor = getPnlColor(group.stats.totalPnl || '0') const orders = group.orders as SellOrderInfo[] return ( {/* 分组头部 */}
toggleMarket(group.marketId)} >
{t('copyTradingOrders.orderCount') || '订单数'}: {group.stats.count} {t('copyTradingOrders.totalAmount') || '总金额'}: {formatUSDC(group.stats.totalAmount)} USDC {group.stats.totalPnl && ( {t('copyTradingOrders.totalPnl') || '总盈亏'}: {formatUSDC(group.stats.totalPnl)} USDC )}
{/* 订单列表 */} {isExpanded && ( <> {isMobile ? (
{orders.map((order) => { const date = new Date(order.createdAt) const formattedDate = date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) const amount = (parseFloat(order.quantity) * parseFloat(order.price)).toString() return (
{order.orderId.slice(0, 8)}...{order.orderId.slice(-6)} {!isAutoGeneratedOrderId(order.orderId) && (
{t('copyTradingOrders.quantity') || '数量'}: {formatUSDC(order.quantity)} | {t('copyTradingOrders.price') || '价格'}: {formatUSDC(order.price)}
{t('copyTradingOrders.amount') || '金额'}: {formatUSDC(amount)} USDC
{t('copyTradingOrders.realizedPnl') || '已实现盈亏'}: {formatUSDC(order.realizedPnl)} USDC
{formattedDate}
) })}
) : ( )} )} ) })} ) } // 渲染列表视图 const renderListView = () => { if (isMobile) { return (
{loading ? (
) : orders.length === 0 ? (
{t('copyTradingOrders.noSellOrders') || '暂无卖出订单'}
) : (
{orders.map((order) => { const date = new Date(order.createdAt) const formattedDate = date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) const amount = (parseFloat(order.quantity) * parseFloat(order.price)).toString() return (
{order.orderId.slice(0, 8)}...{order.orderId.slice(-6)} {!isAutoGeneratedOrderId(order.orderId) && (
{t('copyTradingOrders.sellInfo') || '卖出信息'}
{t('copyTradingOrders.quantity') || '数量'}: {formatUSDC(order.quantity)} | {t('copyTradingOrders.price') || '价格'}: {formatUSDC(order.price)}
{t('copyTradingOrders.amount') || '金额'}: {formatUSDC(amount)} USDC
{t('copyTradingOrders.realizedPnl') || '已实现盈亏'}
{formatUSDC(order.realizedPnl)} USDC
{t('copyTradingOrders.leaderTradeId') || 'Leader 交易ID'}
{order.leaderTradeId.slice(0, 8)}...{order.leaderTradeId.slice(-6)}
{t('copyTradingOrders.market') || '市场'}
{order.marketTitle ? ( (() => { const marketUrl = getPolymarketUrl(order.marketSlug, order.eventSlug, order.marketCategory, order.marketId) return marketUrl ? ( {order.marketTitle} ) : (
{order.marketTitle}
) })() ) : null}
{order.marketId.slice(0, 8)}...{order.marketId.slice(-6)}
{t('copyTradingOrders.createdAt') || '创建时间'}: {formattedDate}
) })}
)}
) } else { return (
`${t('common.total') || '共'} ${total} ${t('common.items') || '条'}`, onChange: (newPage, newLimit) => { setPage(newPage) setLimit(newLimit) } }} /> ) } } return (
setFilters({ ...filters, marketId: e.target.value || undefined })} /> handleMarketTitleChange(e.target.value)} /> {t('copyTradingOrders.groupByMarket') || '按市场分组'}: } unCheckedChildren={} /> {groupByMarket && groupedOrders.length > 0 && ( )} {groupByMarket && (
{t('common.total') || '共'} {groupedTotal} {t('copyTradingOrders.markets') || '个市场'}
)}
{loading ? (
) : groupByMarket ? ( <> {renderGroupedView()} {groupedOrders.length > 0 && (
{t('common.pageOf') || '第'} {page} / {Math.ceil(groupedTotal / limit)} {t('common.page') || '页'}
)} ) : ( renderListView() )}
) } export default SellOrdersTab