diff --git a/frontend/src/components/TradeList.css b/frontend/src/components/TradeList.css index f13b865..5699796 100644 --- a/frontend/src/components/TradeList.css +++ b/frontend/src/components/TradeList.css @@ -208,6 +208,31 @@ background: #5a6268; } +.btn-export { + padding: 0.75rem 1.5rem; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 0.9rem; + font-weight: 500; + transition: all 0.3s; + touch-action: manipulation; + min-height: 44px; + background: #28a745; + color: white; +} + +@media (min-width: 768px) { + .btn-export { + padding: 0.5rem 1.5rem; + min-height: auto; + } +} + +.btn-export:hover { + background: #218838; +} + .no-data { text-align: center; padding: 3rem; @@ -257,11 +282,36 @@ color: #e74c3c; } +.table-wrapper { + width: 100%; + overflow-x: auto; + margin-top: 1rem; + -webkit-overflow-scrolling: touch; +} + +.table-wrapper::-webkit-scrollbar { + height: 8px; +} + +.table-wrapper::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 4px; +} + +.table-wrapper::-webkit-scrollbar-thumb { + background: #888; + border-radius: 4px; +} + +.table-wrapper::-webkit-scrollbar-thumb:hover { + background: #555; +} + .trades-table { width: 100%; border-collapse: collapse; - margin-top: 1rem; display: none; + min-width: 1200px; /* 确保表格有最小宽度,避免列被压缩 */ } @media (min-width: 768px) { @@ -273,16 +323,100 @@ .trades-table th { background-color: #34495e; color: white; - padding: 1rem; + padding: 0.75rem 0.5rem; text-align: left; font-weight: 500; - font-size: 0.9rem; + font-size: 0.85rem; + white-space: nowrap; + position: sticky; + top: 0; + z-index: 10; } .trades-table td { - padding: 0.75rem 1rem; + padding: 0.6rem 0.5rem; border-bottom: 1px solid #eee; - font-size: 0.9rem; + font-size: 0.85rem; + white-space: nowrap; +} + +/* 优化特定列的宽度 */ +.trades-table th:nth-child(1), +.trades-table td:nth-child(1) { + min-width: 60px; + max-width: 80px; +} + +.trades-table th:nth-child(2), +.trades-table td:nth-child(2) { + min-width: 90px; +} + +.trades-table th:nth-child(3), +.trades-table td:nth-child(3) { + min-width: 60px; +} + +.trades-table th:nth-child(4), +.trades-table td:nth-child(4) { + min-width: 90px; +} + +.trades-table th:nth-child(5), +.trades-table td:nth-child(5) { + min-width: 90px; +} + +.trades-table th:nth-child(6), +.trades-table td:nth-child(6) { + min-width: 90px; +} + +.trades-table th:nth-child(7), +.trades-table td:nth-child(7) { + min-width: 90px; +} + +.trades-table th:nth-child(8), +.trades-table td:nth-child(8) { + min-width: 90px; +} + +.trades-table th:nth-child(9), +.trades-table td:nth-child(9) { + min-width: 100px; +} + +.trades-table th:nth-child(10), +.trades-table td:nth-child(10) { + min-width: 100px; +} + +.trades-table th:nth-child(11), +.trades-table td:nth-child(11) { + min-width: 80px; +} + +.trades-table th:nth-child(12), +.trades-table td:nth-child(12) { + min-width: 100px; +} + +.trades-table th:nth-child(13), +.trades-table td:nth-child(13) { + min-width: 200px; + white-space: normal; + word-break: break-all; +} + +.trades-table th:nth-child(14), +.trades-table td:nth-child(14) { + min-width: 140px; +} + +.trades-table th:nth-child(15), +.trades-table td:nth-child(15) { + min-width: 140px; } .trades-table tr:hover { diff --git a/frontend/src/components/TradeList.jsx b/frontend/src/components/TradeList.jsx index e0d3d8d..9914171 100644 --- a/frontend/src/components/TradeList.jsx +++ b/frontend/src/components/TradeList.jsx @@ -81,6 +81,65 @@ const TradeList = () => { setUseCustomDate(false) } + // 导出当前订单数据 + const handleExport = () => { + if (trades.length === 0) { + alert('暂无数据可导出') + return + } + + // 准备导出数据 + const exportData = trades.map(trade => { + const notional = trade.notional_usdt !== undefined && trade.notional_usdt !== null + ? parseFloat(trade.notional_usdt) + : (trade.entry_value_usdt !== undefined && trade.entry_value_usdt !== null + ? parseFloat(trade.entry_value_usdt) + : (parseFloat(trade.quantity || 0) * parseFloat(trade.entry_price || 0))) + const leverage = parseFloat(trade.leverage || 10) + const margin = trade.margin_usdt !== undefined && trade.margin_usdt !== null + ? parseFloat(trade.margin_usdt) + : (leverage > 0 ? notional / leverage : 0) + const pnl = parseFloat(trade.pnl || 0) + const pnlPercent = margin > 0 ? (pnl / margin) * 100 : 0 + + return { + 交易ID: trade.id, + 交易对: trade.symbol, + 方向: trade.side, + 数量: parseFloat(trade.quantity || 0), + 名义价值: notional, + 保证金: margin, + 杠杆: leverage, + 入场价: parseFloat(trade.entry_price || 0), + 出场价: trade.exit_price ? parseFloat(trade.exit_price) : null, + 盈亏: pnl, + 盈亏比例: pnlPercent, + 状态: trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消', + 平仓类型: trade.exit_reason_display || '-', + 开仓订单号: trade.entry_order_id || '-', + 平仓订单号: trade.exit_order_id || '-', + 入场时间: trade.entry_time, + 平仓时间: trade.exit_time || null, + } + }) + + // 生成文件名 + const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-') + const filename = `交易记录_${timestamp}.json` + + // 创建并下载文件 + const dataStr = JSON.stringify(exportData, null, 2) + const dataBlob = new Blob([dataStr], { type: 'application/json' }) + const url = URL.createObjectURL(dataBlob) + const link = document.createElement('a') + link.href = url + link.download = filename + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) + } + if (loading) return